1. Project Clover database Sat Apr 27 2024 21:29:13 CDT
  2. Package net.bytebuddy.asm

File Advice.java

 

Coverage histogram

../../../img/srcFileCovDistChart6.png
93% of files have more coverage

Code metrics

482
1,391
461
123
7,370
4,016
888
0.64
3.02
3.75
1.93

Classes

Class Line # Actions
Advice 111 53 0% 31 2
0.97802297.8%
Advice.MethodSizeHandler 359 0 - 0 0
-1.0 -
Advice.MethodSizeHandler.ForAdvice 402 0 - 0 0
-1.0 -
Advice.MethodSizeHandler.NoOp 431 5 0% 8 11
0.1538461615.4%
Advice.MethodSizeHandler.Default 483 11 0% 9 6
0.7272727572.7%
Advice.MethodSizeHandler.Default.ForAdvice 581 9 0% 5 4
0.7142857371.4%
Advice.StackMapFrameHandler 653 0 - 0 0
-1.0 -
Advice.StackMapFrameHandler.ForInstrumentedMethod 692 0 - 0 0
-1.0 -
Advice.StackMapFrameHandler.ForAdvice 721 0 - 0 0
-1.0 -
Advice.StackMapFrameHandler.NoOp 728 4 0% 8 10
0.1666666716.7%
Advice.StackMapFrameHandler.Default 785 74 0% 48 70
0.4308943243.1%
Advice.StackMapFrameHandler.Default.TranslationMode 1063 16 0% 8 26
0.0714285757.1%
Advice.StackMapFrameHandler.Default.ForAdvice 1146 23 0% 19 16
0.609756161%
Advice.AdviceVisitor 1263 20 0% 11 5
0.8529411685.3%
Advice.AdviceVisitor.WithoutExitAdvice 1403 2 0% 5 4
0.4285714342.9%
Advice.AdviceVisitor.WithExitAdvice 1457 25 0% 11 11
0.6562565.6%
Advice.AdviceVisitor.WithExitAdvice.WithoutExceptionHandling 1536 4 0% 8 3
0.7857142778.6%
Advice.AdviceVisitor.WithExitAdvice.WithExceptionHandling 1601 39 0% 17 58
0.00%
Advice.Dispatcher 1728 0 - 0 0
-1.0 -
Advice.Dispatcher.Unresolved 1750 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping 1785 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping.Context 1799 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping.Context.ForMethodEntry 1819 5 0% 6 4
0.666666766.7%
Advice.Dispatcher.OffsetMapping.Context.ForMethodExit 1876 13 0% 8 8
0.555555655.6%
Advice.Dispatcher.OffsetMapping.Target 1946 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping.Target.ForDefaultValue 1974 30 0% 13 31
0.0606060626.1%
Advice.Dispatcher.OffsetMapping.Target.ForParameter 2028 26 0% 17 11
0.676470667.6%
Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadOnly 2091 4 0% 4 2
0.7575%
Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadWrite 2123 6 0% 6 9
0.3076923230.8%
Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadWrite.WithCasting 2167 16 0% 9 3
0.888888988.9%
Advice.Dispatcher.OffsetMapping.Target.ForField 2221 29 0% 19 21
0.47547.5%
Advice.Dispatcher.OffsetMapping.Target.ForField.ReadOnly 2309 5 0% 5 4
0.660%
Advice.Dispatcher.OffsetMapping.Target.ForField.ReadWrite 2346 31 0% 8 38
0.09523819.5%
Advice.Dispatcher.OffsetMapping.Target.ForConstantPoolValue 2416 26 0% 19 15
0.583333358.3%
Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter 2482 21 0% 10 12
0.660%
Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.ReadOnly 2549 4 0% 4 4
0.550%
Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.ReadWrite 2589 4 0% 4 4
0.550%
Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.BoxingDispatcher 2629 31 0% 14 51
0.037735853.8%
Advice.Dispatcher.OffsetMapping.Target.ForBoxedArguments 2800 51 0% 20 51
0.2525%
Advice.Dispatcher.OffsetMapping.Target.ForNullConstant 2906 14 0% 7 17
0.1052631610.5%
Advice.Dispatcher.OffsetMapping.Target.ForSerializedObject 2964 37 0% 12 28
0.4166666641.7%
Advice.Dispatcher.OffsetMapping.Factory 3084 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping.ForParameter 3103 23 0% 16 2
0.9534883595.3%
Advice.Dispatcher.OffsetMapping.ForParameter.Factory 3188 8 0% 6 2
0.866666786.7%
Advice.Dispatcher.OffsetMapping.ForThisReference 3236 25 0% 21 7
0.8653846486.5%
Advice.Dispatcher.OffsetMapping.ForThisReference.Factory 3321 8 0% 6 2
0.866666786.7%
Advice.Dispatcher.OffsetMapping.ForInstrumentedType 3371 2 0% 2 2
0.550%
Advice.Dispatcher.OffsetMapping.ForField 3392 29 0% 19 6
0.8846153688.5%
Advice.Dispatcher.OffsetMapping.ForField.WithImplicitType 3490 3 0% 3 0
1.0100%
Advice.Dispatcher.OffsetMapping.ForField.WithExplicitType 3520 17 0% 10 0
1.0100%
Advice.Dispatcher.OffsetMapping.ForField.Factory 3577 11 0% 7 4
0.880%
Advice.Dispatcher.OffsetMapping.ForOrigin 3630 46 0% 21 23
0.64062564.1%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer 3735 0 - 0 0
-1.0 -
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForMethodName 3748 2 0% 2 2
0.550%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForTypeName 3774 2 0% 2 2
0.550%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForDescriptor 3800 2 0% 2 2
0.550%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForJavaSignature 3826 9 0% 3 11
0.1538461615.4%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForReturnTypeName 3862 2 0% 2 2
0.550%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForStringRepresentation 3888 2 0% 2 0
1.0100%
Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForConstantValue 3909 10 0% 8 2
0.894736889.5%
Advice.Dispatcher.OffsetMapping.ForOrigin.Factory 3955 9 0% 5 2
0.8823529588.2%
Advice.Dispatcher.OffsetMapping.ForIgnored 3986 3 0% 4 3
0.62562.5%
Advice.Dispatcher.OffsetMapping.ForEnterValue 4014 4 0% 6 4
0.666666766.7%
Advice.Dispatcher.OffsetMapping.ForEnterValue.Factory 4067 23 0% 16 5
0.8809523688.1%
Advice.Dispatcher.OffsetMapping.ForReturnValue 4136 15 0% 14 2
0.937593.8%
Advice.Dispatcher.OffsetMapping.ForReturnValue.Factory 4195 8 0% 6 4
0.7333333573.3%
Advice.Dispatcher.OffsetMapping.ForBoxedReturnValue 4243 7 0% 8 18
0.110%
Advice.Dispatcher.OffsetMapping.ForBoxedReturnValue.Factory 4294 10 0% 8 14
0.3333333433.3%
Advice.Dispatcher.OffsetMapping.ForBoxedArguments 4346 7 0% 5 8
0.4285714342.9%
Advice.Dispatcher.OffsetMapping.ForThrowable 4378 16 0% 10 6
0.7931034679.3%
Advice.Dispatcher.OffsetMapping.ForThrowable.Factory 4446 22 0% 15 34
0.190476219%
Advice.Dispatcher.OffsetMapping.ForUserValue 4530 36 0% 27 28
0.5820895458.2%
Advice.Dispatcher.OffsetMapping.ForUserValue.Factory 4625 15 0% 10 1
0.96296396.3%
Advice.Dispatcher.OffsetMapping.Illegal 4696 14 0% 10 0
1.0100%
Advice.Dispatcher.SuppressionHandler 4757 0 - 0 0
-1.0 -
Advice.Dispatcher.SuppressionHandler.ReturnValueProducer 4769 0 - 0 0
-1.0 -
Advice.Dispatcher.SuppressionHandler.Bound 4780 0 - 0 0
-1.0 -
Advice.Dispatcher.SuppressionHandler.NoOp 4820 2 0% 6 1
0.87587.5%
Advice.Dispatcher.SuppressionHandler.Suppressing 4861 11 0% 10 3
0.869565287%
Advice.Dispatcher.SuppressionHandler.Suppressing.Bound 4917 15 0% 6 15
0.285714328.6%
Advice.Dispatcher.Resolved 4987 0 - 0 0
-1.0 -
Advice.Dispatcher.Resolved.ForMethodEnter 5006 0 - 0 0
-1.0 -
Advice.Dispatcher.Resolved.ForMethodExit 5020 0 - 0 0
-1.0 -
Advice.Dispatcher.Bound 5035 0 - 0 0
-1.0 -
Advice.Dispatcher.Inactive 5051 8 0% 10 2
0.888888988.9%
Advice.Dispatcher.Inlining 5118 8 0% 8 0
1.0100%
Advice.Dispatcher.Inlining.Resolved 5177 24 0% 11 15
0.6153846461.5%
Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner 5291 13 0% 7 0
1.0100%
Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableExtractor 5388 3 0% 5 0
1.0100%
Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableCollector 5416 6 0% 4 2
0.880%
Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableSubstitutor 5457 18 0% 11 15
0.550%
Advice.Dispatcher.Inlining.Resolved.ForMethodEnter 5553 7 0% 4 2
0.818181881.8%
Advice.Dispatcher.Inlining.Resolved.ForMethodExit 5614 12 0% 6 6
0.6842105468.4%
Advice.Dispatcher.Inlining.Resolved.ForMethodExit.WithExceptionHandler 5718 5 0% 4 4
0.555555655.6%
Advice.Dispatcher.Inlining.Resolved.ForMethodExit.WithoutExceptionHandler 5767 4 0% 4 2
0.7575%
Advice.Dispatcher.Inlining.CodeTranslationVisitor 5809 27 0% 15 13
0.7045454470.5%
Advice.Dispatcher.Inlining.CodeTranslationVisitor.ForMethodEnter 5967 28 0% 22 27
0.4130434741.3%
Advice.Dispatcher.Inlining.CodeTranslationVisitor.ForMethodExit 6051 20 0% 12 16
0.384615438.5%
Advice.Dispatcher.Delegating 6139 8 0% 8 6
0.62562.5%
Advice.Dispatcher.Delegating.Resolved 6198 25 0% 12 22
0.4761904847.6%
Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter 6298 20 0% 4 24
0.00%
Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter.ForMethodEnter 6411 27 0% 22 51
0.00%
Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter.ForMethodExit 6489 13 0% 7 17
0.00%
Advice.Dispatcher.Delegating.Resolved.ForMethodEnter 6546 7 0% 4 7
0.3636363736.4%
Advice.Dispatcher.Delegating.Resolved.ForMethodExit 6602 12 0% 6 15
0.2105263221.1%
Advice.Dispatcher.Delegating.Resolved.ForMethodExit.WithExceptionHandler 6692 4 0% 3 7
0.00%
Advice.Dispatcher.Delegating.Resolved.ForMethodExit.WithoutExceptionHandler 6734 3 0% 3 6
0.00%
Advice.OnMethodEnter 6785 0 - 0 0
-1.0 -
Advice.OnMethodExit 6828 0 - 0 0
-1.0 -
Advice.Argument 6870 0 - 0 0
-1.0 -
Advice.This 6905 0 - 0 0
-1.0 -
Advice.FieldValue 6946 0 - 0 0
-1.0 -
Advice.Origin 6984 0 - 0 0
-1.0 -
Advice.Ignored 7020 0 - 0 0
-1.0 -
Advice.Enter 7034 0 - 0 0
-1.0 -
Advice.Return 7057 0 - 0 0
-1.0 -
Advice.BoxedReturn 7095 0 - 0 0
-1.0 -
Advice.BoxedArguments 7133 0 - 0 0
-1.0 -
Advice.Thrown 7148 0 - 0 0
-1.0 -
Advice.DynamicValue 7178 0 - 0 0
-1.0 -
Advice.DynamicValue.ForFixedValue 7204 10 0% 10 4
0.8260869482.6%
Advice.WithCustomMapping 7253 22 0% 14 0
1.0100%
Advice.NoExceptionHandler 7356 1 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 285 tests. .

Source view

1    package net.bytebuddy.asm;
2   
3    import net.bytebuddy.ClassFileVersion;
4    import net.bytebuddy.description.annotation.AnnotationDescription;
5    import net.bytebuddy.description.field.FieldDescription;
6    import net.bytebuddy.description.method.MethodDescription;
7    import net.bytebuddy.description.method.MethodList;
8    import net.bytebuddy.description.method.ParameterDescription;
9    import net.bytebuddy.description.method.ParameterList;
10    import net.bytebuddy.description.type.TypeDefinition;
11    import net.bytebuddy.description.type.TypeDescription;
12    import net.bytebuddy.description.type.TypeList;
13    import net.bytebuddy.dynamic.ClassFileLocator;
14    import net.bytebuddy.dynamic.scaffold.FieldLocator;
15    import net.bytebuddy.implementation.bytecode.StackSize;
16    import net.bytebuddy.matcher.ElementMatcher;
17    import net.bytebuddy.utility.CompoundList;
18    import net.bytebuddy.utility.ExceptionTableSensitiveMethodVisitor;
19    import org.objectweb.asm.*;
20   
21    import java.io.*;
22    import java.lang.annotation.*;
23    import java.util.*;
24   
25    import static net.bytebuddy.matcher.ElementMatchers.named;
26   
27    /**
28    * <p>
29    * Advice wrappers copy the code of blueprint methods to be executed before and/or after a matched method. To achieve this, a {@code static}
30    * method of a class is annotated by {@link OnMethodEnter} and/or {@link OnMethodExit} and provided to an instance of this class.
31    * </p>
32    * <p>
33    * A method that is annotated with {@link OnMethodEnter} can annotate its parameters with {@link Argument} where field access to this parameter
34    * is substituted with access to the specified argument of the instrumented method. Alternatively, a parameter can be annotated by {@link This}
35    * where the {@code this} reference of the instrumented method is read when the parameter is accessed. This mechanism can also be used to assign a
36    * new value to the {@code this} reference of an instrumented method. If no annotation is used on a parameter, it is assigned the {@code n}-th
37    * parameter of the instrumented method for the {@code n}-th parameter of the advice method. All parameters must declare the exact same type as
38    * the parameters of the instrumented type or the method's declaring type for the {@link This} reference respectively if they are not marked as
39    * <i>read-only</i>. In the latter case, it suffices that a parameter type is a super type of the corresponding type of the instrumented method.
40    * </p>
41    * <p>
42    * A method that is annotated with {@link OnMethodExit} can equally annotate its parameters with {@link Argument} and {@link This}. Additionally,
43    * it can annotate a parameter with {@link Return} to receive the original method's return value. By reassigning the return value, it is possible
44    * to replace the returned value. If an instrumented method does not return a value, this annotation must not be used. If a method returns
45    * exceptionally, the parameter is set to its default value, i.e. to {@code 0} for primitive types and to {@code null} for reference types. The
46    * parameter's type must be equal to the instrumented method's return type if it is not set to <i>read-only</i> where it suffices to declare the
47    * parameter type to be of any super type to the instrumented method's return type. An exception can be read by annotating a parameter of type
48    * {@link Throwable} annotated with {@link Thrown} which is assigned the thrown {@link Throwable} or {@code null} if a method returns normally.
49    * Doing so, it is possible to exchange a thrown exception with any checked or unchecked exception.Finally, if a method annotated with
50    * {@link OnMethodEnter} exists and this method returns a value, this value can be accessed by a parameter annotated with {@link Enter}.
51    * This parameter must declare the same type as type being returned by the method annotated with {@link OnMethodEnter}. If the parameter is marked
52    * to be <i>read-only</i>, it suffices that the annotated parameter is of a super type of the return type of the method annotated by
53    * {@link OnMethodEnter}. If no such method exists or this method returns {@code void}, no such parameter must be declared. Any return value
54    * of a method that is annotated by {@link OnMethodExit} is discarded.
55    * </p>
56    * <p>
57    * If any advice method throws an exception, the method is terminated prematurely. If the method annotated by {@link OnMethodEnter} throws an exception,
58    * the method annotated by {@link OnMethodExit} method is not invoked. If the instrumented method throws an exception, the method that is annotated by
59    * {@link OnMethodExit} is only invoked if the {@link OnMethodExit#onThrowable()} property is set to {@code true} what is the default. If this property
60    * is set to {@code false}, the {@link Thrown} annotation must not be used on any parameter.
61    * </p>
62    * <p>
63    * Byte Buddy does not assert the visibility of any types that are referenced within an inlined advice method. It is the responsibility of
64    * the user of this class to assure that all types referenced within the advice methods are visible to the instrumented class. Failing to
65    * do so results in a {@link IllegalAccessError} at the instrumented class's runtime.
66    * </p>
67    * <p>
68    * Advice cannot be applied to native or abstract methods which are silently skipped when advice matches such a method.
69    * </p>
70    * <p>
71    * <b>Important</b>: Since Java 6, class files contain <i>stack map frames</i> embedded into a method's byte code. When advice methods are compiled
72    * with a class file version less then Java 6 but are used for a class file that was compiled to Java 6 or newer, these stack map frames must be
73    * computed by ASM by using the {@link ClassWriter#COMPUTE_FRAMES} option. If the advice methods do not contain any branching instructions, this is
74    * not required. No action is required if the advice methods are at least compiled with Java 6 but are used on classes older than Java 6. This
75    * limitation only applies to advice methods that are inlined. Also, it is the responsibility of this class's user to assure that the advice method
76    * does not contain byte code constructs that are not supported by the class containing the instrumented method. In particular, pre Java-5
77    * try-finally blocks cannot be inlined into classes with newer byte code levels as the <i>jsr</i> instruction was deprecated. Also, classes prior
78    * to Java 7 do not support the <i>invokedynamic</i> command which must not be contained by an advice method if the instrumented method targets an
79    * older class file format version.
80    * </p>
81    * <p>
82    * <b>Note</b>: For the purpose of inlining, Java 5 and Java 6 byte code can be seen as the best candidate for advice methods. These versions do
83    * no longer allow subroutines, neither do they already allow invokedynamic instructions or method handles. This way, Java 5 and Java 6 byte
84    * code is compatible to both older and newer versions. One exception for backwards-incompatible byte code is the possibility to load type references
85    * from the constant pool onto the operand stack. These instructions can however easily be transformerd for classes compiled to Java 4 and older
86    * by registering a {@link TypeConstantAdjustment} <b>before</b> the advice visitor.
87    * </p>
88    * <p>
89    * <b>Note</b>: It is not possible to trigger break points in inlined advice methods as the debugging information of the inlined advice is not
90    * preserved. It is not possible in Java to reference more than one source file per class what makes translating such debugging information
91    * impossible. It is however possible to set break points in advice methods when invoking the original advice target. This allows debugging
92    * of advice code within unit tests that invoke the advice method without instrumentation. As a conequence of not transferring debugging information,
93    * the names of the parameters of an advice method do not matter when inlining, neither does any meta information on the advice method's body
94    * such as annotations or parameter modifiers.
95    * </p>
96    *
97    * @see Argument
98    * @see BoxedArguments
99    * @see BoxedReturn
100    * @see DynamicValue
101    * @see Enter
102    * @see FieldValue
103    * @see Ignored
104    * @see OnMethodEnter
105    * @see OnMethodExit
106    * @see Origin
107    * @see Return
108    * @see This
109    * @see Thrown
110    */
 
111    public class Advice implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
112   
113    /**
114    * A reference to the {@link OnMethodEnter#inline()} method.
115    */
116    private static final MethodDescription.InDefinedShape INLINE_ENTER;
117   
118    /**
119    * A reference to the {@link OnMethodEnter#suppress()} method.
120    */
121    private static final MethodDescription.InDefinedShape SUPPRESS_ENTER;
122   
123    /**
124    * A reference to the {@link OnMethodExit#inline()} method.
125    */
126    private static final MethodDescription.InDefinedShape INLINE_EXIT;
127   
128    /**
129    * A reference to the {@link OnMethodExit#suppress()} method.
130    */
131    private static final MethodDescription.InDefinedShape SUPPRESS_EXIT;
132   
133    /**
134    * A reference to the {@link OnMethodExit#onThrowable()} method.
135    */
136    private static final MethodDescription.InDefinedShape ON_THROWABLE;
137   
138    /*
139    * Extracts the annotation values for the enter and exit advice annotations.
140    */
 
141  1 toggle static {
142  1 MethodList<MethodDescription.InDefinedShape> enter = new TypeDescription.ForLoadedType(OnMethodEnter.class).getDeclaredMethods();
143  1 INLINE_ENTER = enter.filter(named("inline")).getOnly();
144  1 SUPPRESS_ENTER = enter.filter(named("suppress")).getOnly();
145  1 MethodList<MethodDescription.InDefinedShape> exit = new TypeDescription.ForLoadedType(OnMethodExit.class).getDeclaredMethods();
146  1 INLINE_EXIT = exit.filter(named("inline")).getOnly();
147  1 SUPPRESS_EXIT = exit.filter(named("suppress")).getOnly();
148  1 ON_THROWABLE = exit.filter(named("onThrowable")).getOnly();
149    }
150   
151    /**
152    * The dispatcher for instrumenting the instrumented method upon entering.
153    */
154    private final Dispatcher.Resolved.ForMethodEnter methodEnter;
155   
156    /**
157    * The dispatcher for instrumenting the instrumented method upon exiting.
158    */
159    private final Dispatcher.Resolved.ForMethodExit methodExit;
160   
161    /**
162    * Creates a new advice.
163    *
164    * @param methodEnter The dispatcher for instrumenting the instrumented method upon entering.
165    * @param methodExit The dispatcher for instrumenting the instrumented method upon exiting.
166    */
 
167  304 toggle protected Advice(Dispatcher.Resolved.ForMethodEnter methodEnter, Dispatcher.Resolved.ForMethodExit methodExit) {
168  304 this.methodEnter = methodEnter;
169  304 this.methodExit = methodExit;
170    }
171   
172    /**
173    * Implements advice where every matched method is advised by the given type's advisory methods. The advices binary representation is
174    * accessed by querying the class loader of the supplied class for a class file.
175    *
176    * @param type The type declaring the advice.
177    * @return A method visitor wrapper representing the supplied advice.
178    */
 
179  270 toggle public static Advice to(Class<?> type) {
180  270 return to(type, ClassFileLocator.ForClassLoader.of(type.getClassLoader()));
181    }
182   
183    /**
184    * Implements advice where every matched method is advised by the given type's advisory methods.
185    *
186    * @param type The type declaring the advice.
187    * @param classFileLocator The class file locator for locating the advisory class's class file.
188    * @return A method visitor wrapper representing the supplied advice.
189    */
 
190  271 toggle public static Advice to(Class<?> type, ClassFileLocator classFileLocator) {
191  271 return to(new TypeDescription.ForLoadedType(type), classFileLocator);
192    }
193   
194    /**
195    * Implements advice where every matched method is advised by the given type's advisory methods. Using this method, a non-operational
196    * class file locator is specified for the advice target. This implies that only advice targets with the <i>inline</i> target set
197    * to {@code false} are resolvable by the returned instance.
198    *
199    * @param typeDescription The type declaring the advice.
200    * @return A method visitor wrapper representing the supplied advice.
201    */
 
202  1 toggle public static Advice to(TypeDescription typeDescription) {
203  1 return to(typeDescription, ClassFileLocator.NoOp.INSTANCE);
204    }
205   
206    /**
207    * Implements advice where every matched method is advised by the given type's advisory methods.
208    *
209    * @param typeDescription A description of the type declaring the advice.
210    * @param classFileLocator The class file locator for locating the advisory class's class file.
211    * @return A method visitor wrapper representing the supplied advice.
212    */
 
213  272 toggle public static Advice to(TypeDescription typeDescription, ClassFileLocator classFileLocator) {
214  272 return to(typeDescription, classFileLocator, Collections.<Dispatcher.OffsetMapping.Factory>emptyList());
215    }
216   
217    /**
218    * Creates a new advice.
219    *
220    * @param typeDescription A description of the type declaring the advice.
221    * @param classFileLocator The class file locator for locating the advisory class's class file.
222    * @param userFactories A list of custom factories for user generated offset mappings.
223    * @return A method visitor wrapper representing the supplied advice.
224    */
 
225  318 toggle protected static Advice to(TypeDescription typeDescription,
226    ClassFileLocator classFileLocator,
227    List<? extends Dispatcher.OffsetMapping.Factory> userFactories) {
228  318 try {
229  318 Dispatcher.Unresolved methodEnter = Dispatcher.Inactive.INSTANCE, methodExit = Dispatcher.Inactive.INSTANCE;
230  318 for (MethodDescription.InDefinedShape methodDescription : typeDescription.getDeclaredMethods()) {
231  912 methodEnter = locate(OnMethodEnter.class, INLINE_ENTER, methodEnter, methodDescription);
232  910 methodExit = locate(OnMethodExit.class, INLINE_EXIT, methodExit, methodDescription);
233    }
234  316 if (!methodEnter.isAlive() && !methodExit.isAlive()) {
235  1 throw new IllegalArgumentException("No advice defined by " + typeDescription);
236    }
237  315 ClassFileLocator.Resolution binaryRepresentation = methodEnter.isBinary() || methodExit.isBinary()
238    ? classFileLocator.locate(typeDescription.getName())
239    : new ClassFileLocator.Resolution.Illegal(typeDescription.getName());
240  314 Dispatcher.Resolved.ForMethodEnter resolved = methodEnter.asMethodEnter(userFactories, binaryRepresentation);
241  309 return new Advice(resolved, methodExit.asMethodExitTo(userFactories, binaryRepresentation, resolved));
242    } catch (IOException exception) {
243  1 throw new IllegalStateException("Error reading class file of " + typeDescription, exception);
244    }
245    }
246   
247    /**
248    * Locates a dispatcher for the method if available.
249    *
250    * @param type The annotation type that indicates a given form of advice that is currently resolved.
251    * @param property An annotation property that indicates if the advice method should be inlined.
252    * @param dispatcher Any previous dispatcher that was discovered or {@code null} if no such dispatcher was yet found.
253    * @param methodDescription The method description that is considered as an advice method.
254    * @return A resolved dispatcher or {@code null} if no dispatcher was resolved.
255    */
 
256  1822 toggle private static Dispatcher.Unresolved locate(Class<? extends Annotation> type,
257    MethodDescription.InDefinedShape property,
258    Dispatcher.Unresolved dispatcher,
259    MethodDescription.InDefinedShape methodDescription) {
260  1822 AnnotationDescription annotation = methodDescription.getDeclaredAnnotations().ofType(type);
261  1822 if (annotation == null) {
262  1342 return dispatcher;
263  480 } else if (dispatcher.isAlive()) {
264  1 throw new IllegalStateException("Duplicate advice for " + dispatcher + " and " + methodDescription);
265  479 } else if (!methodDescription.isStatic()) {
266  1 throw new IllegalStateException("Advice for " + methodDescription + " is not static");
267    } else {
268  478 return annotation.getValue(property, Boolean.class)
269    ? new Dispatcher.Inlining(methodDescription)
270    : new Dispatcher.Delegating(methodDescription);
271    }
272    }
273   
274    /**
275    * Allows for the configuration of custom annotations that are then bound to a dynamically computed, constant value.
276    *
277    * @return A builder for an {@link Advice} instrumentation with custom values.
278    * @see DynamicValue
279    */
 
280  48 toggle public static WithCustomMapping withCustomMapping() {
281  48 return new WithCustomMapping();
282    }
283   
284    /**
285    * Returns an ASM visitor wrapper that matches the given matcher and applies this advice to the matched methods.
286    *
287    * @param matcher The matcher identifying methods to apply the advice to.
288    * @return A suitable ASM visitor wrapper with the <i>compute frames</i> option enabled.
289    */
 
290  298 toggle public AsmVisitorWrapper.ForDeclaredMethods on(ElementMatcher<? super MethodDescription.InDefinedShape> matcher) {
291  298 return new AsmVisitorWrapper.ForDeclaredMethods().method(matcher, this);
292    }
293   
 
294  316 toggle @Override
295    public MethodVisitor wrap(TypeDescription instrumentedType,
296    MethodDescription.InDefinedShape methodDescription,
297    MethodVisitor methodVisitor,
298    ClassFileVersion classFileVersion,
299    int writerFlags,
300    int readerFlags) {
301  316 if (methodDescription.isAbstract() || methodDescription.isNative()) {
302  2 return methodVisitor;
303  314 } else if (!methodExit.isAlive()) {
304  45 return new AdviceVisitor.WithoutExitAdvice(methodVisitor,
305    methodDescription,
306    methodEnter,
307    classFileVersion,
308    writerFlags,
309    readerFlags);
310  269 } else if (methodExit.getTriggeringThrowable().represents(NoExceptionHandler.class)) {
311  155 return new AdviceVisitor.WithExitAdvice.WithoutExceptionHandling(methodVisitor,
312    methodDescription,
313    methodEnter,
314    methodExit,
315    classFileVersion,
316    writerFlags,
317    readerFlags);
318  2 } else if (methodDescription.isConstructor()) {
319  2 throw new IllegalStateException("Cannot catch exception during constructor call for " + methodDescription);
320    } else {
321  0 return new AdviceVisitor.WithExitAdvice.WithExceptionHandling(methodVisitor,
322    methodDescription,
323    methodEnter,
324    methodExit,
325    classFileVersion,
326    writerFlags,
327    readerFlags,
328    methodExit.getTriggeringThrowable());
329    }
330    }
331   
 
332  6 toggle @Override
333    public boolean equals(Object other) {
334  1 if (this == other) return true;
335  2 if (other == null || getClass() != other.getClass()) return false;
336  3 Advice advice = (Advice) other;
337  3 return methodEnter.equals(advice.methodEnter)
338    && methodExit.equals(advice.methodExit);
339    }
340   
 
341  4 toggle @Override
342    public int hashCode() {
343  4 int result = methodEnter.hashCode();
344  4 result = 31 * result + methodExit.hashCode();
345  4 return result;
346    }
347   
 
348  4 toggle @Override
349    public String toString() {
350  4 return "Advice{" +
351    "methodEnter=" + methodEnter +
352    ", methodExit=" + methodExit +
353    '}';
354    }
355   
356    /**
357    * A handler for computing the instrumented method's size.
358    */
 
359    protected interface MethodSizeHandler {
360   
361    /**
362    * Indicates that a size is not computed but handled directly by ASM.
363    */
364    int UNDEFINED_SIZE = Short.MAX_VALUE;
365   
366    /**
367    * Binds a method size handler for the entry advice.
368    *
369    * @param adviceMethod The method representing the entry advice.
370    * @return A method size handler for the entry advice.
371    */
372    ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod);
373   
374    /**
375    * Binds the method size handler for the exit advice.
376    *
377    * @param adviceMethod The method representing the exit advice.
378    * @param skipThrowable {@code true} if the exit advice is not invoked on an exception.
379    * @return A method size handler for the exit advice.
380    */
381    ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod, boolean skipThrowable);
382   
383    /**
384    * Computes a compound stack size for the advice and the translated instrumented method.
385    *
386    * @param stackSize The required stack size of the instrumented method before translation.
387    * @return The stack size required by the instrumented method and its advice methods.
388    */
389    int compoundStackSize(int stackSize);
390   
391    /**
392    * Computes a compound local variable array length for the advice and the translated instrumented method.
393    *
394    * @param localVariableLength The required local variable array length of the instrumented method before translation.
395    * @return The local variable length required by the instrumented method and its advice methods.
396    */
397    int compoundLocalVariableLength(int localVariableLength);
398   
399    /**
400    * A method size handler for an advice method.
401    */
 
402    interface ForAdvice {
403   
404    /**
405    * Records a minimum stack size required by the represented advice method.
406    *
407    * @param stackSize The minimum size required by the represented advice method.
408    */
409    void recordMinimum(int stackSize);
410   
411    /**
412    * Records the maximum values for stack size and local variable array which are required by the advice method
413    * for its individual execution without translation.
414    *
415    * @param stackSize The minimum required stack size.
416    * @param localVariableLength The minimum required length of the local variable array.
417    */
418    void recordMaxima(int stackSize, int localVariableLength);
419   
420    /**
421    * Records a minimum padding additionally to the computed stack size that is required for implementing this advice method.
422    *
423    * @param padding The minimum required padding.
424    */
425    void recordPadding(int padding);
426    }
427   
428    /**
429    * A non-operational method size handler.
430    */
 
431    enum NoOp implements MethodSizeHandler, ForAdvice {
432   
433    /**
434    * The singleton instance.
435    */
436    INSTANCE;
437   
 
438  0 toggle @Override
439    public ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod) {
440  0 return this;
441    }
442   
 
443  0 toggle @Override
444    public ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod, boolean skipThrowable) {
445  0 return this;
446    }
447   
 
448  0 toggle @Override
449    public int compoundStackSize(int stackSize) {
450  0 return UNDEFINED_SIZE;
451    }
452   
 
453  0 toggle @Override
454    public int compoundLocalVariableLength(int localVariableLength) {
455  0 return UNDEFINED_SIZE;
456    }
457   
458   
 
459  0 toggle @Override
460    public void recordMinimum(int stackSize) {
461    /* do nothing */
462    }
463   
 
464  0 toggle @Override
465    public void recordMaxima(int stackSize, int localVariableLength) {
466    /* do nothing */
467    }
468   
 
469  0 toggle @Override
470    public void recordPadding(int padding) {
471    /* do nothing */
472    }
473   
 
474  1 toggle @Override
475    public String toString() {
476  1 return "Advice.MethodSizeHandler.NoOp." + name();
477    }
478    }
479   
480    /**
481    * A default implementation for a method size handler.
482    */
 
483    class Default implements MethodSizeHandler {
484   
485    /**
486    * The instrumented method.
487    */
488    private final MethodDescription.InDefinedShape instrumentedMethod;
489   
490    /**
491    * The list of types that the instrumented method requires in addition to the method parameters.
492    */
493    private final TypeList requiredTypes;
494   
495    /**
496    * A list of types that are yielded by the instrumented method and available to the exit advice.
497    */
498    private final TypeList yieldedTypes;
499   
500    /**
501    * The maximum stack size required by a visited advice method.
502    */
503    private int stackSize;
504   
505    /**
506    * The maximum length of the local variable array required by a visited advice method.
507    */
508    private int localVariableLength;
509   
510    /**
511    * Creates a new default meta data handler that recomputes the space requirements of an instrumented method.
512    *
513    * @param instrumentedMethod The instrumented method.
514    * @param requiredTypes The types this meta data handler expects to be available additionally to the instrumented method's parameters.
515    * @param yieldedTypes The types that are expected to be added after the instrumented method returns.
516    */
 
517  278 toggle protected Default(MethodDescription.InDefinedShape instrumentedMethod, TypeList requiredTypes, TypeList yieldedTypes) {
518  278 this.instrumentedMethod = instrumentedMethod;
519  278 this.requiredTypes = requiredTypes;
520  278 this.yieldedTypes = yieldedTypes;
521    }
522   
523    /**
524    * Creates a method size handler applicable for the given instrumented method.
525    *
526    * @param instrumentedMethod The instrumented method.
527    * @param requiredTypes The list of types that the instrumented method requires in addition to the method parameters.
528    * @param yieldedTypes A list of types that are yielded by the instrumented method and available to the exit advice.
529    * @param writerFlags The flags supplied to the ASM class writer.
530    * @return An appropriate method size handler.
531    */
 
532  312 toggle protected static MethodSizeHandler of(MethodDescription.InDefinedShape instrumentedMethod,
533    List<? extends TypeDescription> requiredTypes,
534    List<? extends TypeDescription> yieldedTypes,
535    int writerFlags) {
536  276 return (writerFlags & (ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES)) != 0
537    ? NoOp.INSTANCE
538    : new Default(instrumentedMethod, new TypeList.Explicit(requiredTypes), new TypeList.Explicit(yieldedTypes));
539    }
540   
 
541  182 toggle @Override
542    public MethodSizeHandler.ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod) {
543  182 stackSize = Math.max(stackSize, adviceMethod.getReturnType().getStackSize().getSize());
544  182 return new ForAdvice(adviceMethod, new TypeList.Empty(), new TypeList.Explicit(requiredTypes));
545    }
546   
 
547  229 toggle @Override
548    public MethodSizeHandler.ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod, boolean skipThrowable) {
549  137 stackSize = Math.max(stackSize, adviceMethod.getReturnType().getStackSize().maximum(skipThrowable
550    ? StackSize.ZERO
551    : StackSize.SINGLE).getSize());
552  229 return new ForAdvice(adviceMethod, new TypeList.Explicit(CompoundList.of(requiredTypes, yieldedTypes)), new TypeList.Empty());
553    }
554   
 
555  0 toggle @Override
556    public int compoundStackSize(int stackSize) {
557  0 return Math.max(this.stackSize, stackSize);
558    }
559   
 
560  0 toggle @Override
561    public int compoundLocalVariableLength(int localVariableLength) {
562  0 return Math.max(this.localVariableLength, localVariableLength
563    + requiredTypes.getStackSize()
564    + yieldedTypes.getStackSize());
565    }
566   
 
567  7 toggle @Override
568    public String toString() {
569  7 return "Advice.MethodSizeHandler.Default{" +
570    "instrumentedMethod=" + instrumentedMethod +
571    ", requiredTypes=" + requiredTypes +
572    ", yieldedTypes=" + yieldedTypes +
573    ", stackSize=" + stackSize +
574    ", localVariableLength=" + localVariableLength +
575    '}';
576    }
577   
578    /**
579    * A method size handler for an advice method.
580    */
 
581    protected class ForAdvice implements MethodSizeHandler.ForAdvice {
582   
583    /**
584    * The advice method.
585    */
586    private final MethodDescription.InDefinedShape adviceMethod;
587   
588    /**
589    * A list of types required by this advice method.
590    */
591    private final TypeList requiredTypes;
592   
593    /**
594    * A list of types yielded by this advice method.
595    */
596    private final TypeList yieldedTypes;
597   
598    /**
599    * The padding that this advice method requires additionally to its computed size.
600    */
601    private int padding;
602   
603    /**
604    * Creates a new method size handler for an advice method.
605    *
606    * @param adviceMethod The advice method.
607    * @param requiredTypes A list of types required by this advice method.
608    * @param yieldedTypes A list of types yielded by this advice method.
609    */
 
610  413 toggle protected ForAdvice(MethodDescription.InDefinedShape adviceMethod, TypeList requiredTypes, TypeList yieldedTypes) {
611  413 this.adviceMethod = adviceMethod;
612  413 this.requiredTypes = requiredTypes;
613  413 this.yieldedTypes = yieldedTypes;
614  413 stackSize = Math.max(stackSize, adviceMethod.getReturnType().getStackSize().getSize());
615    }
616   
 
617  0 toggle @Override
618    public void recordMinimum(int stackSize) {
619  0 Default.this.stackSize = Math.max(Default.this.stackSize, stackSize);
620    }
621   
 
622  396 toggle @Override
623    public void recordMaxima(int stackSize, int localVariableLength) {
624  396 Default.this.stackSize = Math.max(Default.this.stackSize, stackSize) + padding;
625  396 Default.this.localVariableLength = Math.max(Default.this.localVariableLength, localVariableLength
626    - adviceMethod.getStackSize()
627    + instrumentedMethod.getStackSize()
628    + requiredTypes.getStackSize()
629    + yieldedTypes.getStackSize());
630    }
631   
 
632  0 toggle @Override
633    public void recordPadding(int padding) {
634  0 this.padding = Math.max(this.padding, padding);
635    }
636   
 
637  6 toggle @Override
638    public String toString() {
639  6 return "Advice.MethodSizeHandler.Default.ForAdvice{" +
640    "adviceMethod=" + adviceMethod +
641    ", requiredTypes=" + requiredTypes +
642    ", yieldedTypes=" + yieldedTypes +
643    ", padding=" + padding +
644    '}';
645    }
646    }
647    }
648    }
649   
650    /**
651    * A handler for computing and translating stack map frames.
652    */
 
653    protected interface StackMapFrameHandler {
654   
655    /**
656    * Translates a frame.
657    *
658    * @param methodVisitor The method visitor to write the frame to.
659    * @param frameType The frame's type.
660    * @param localVariableLength The local variable length.
661    * @param localVariable An array containing the types of the current local variables.
662    * @param stackSize The size of the operand stack.
663    * @param stack An array containing the types of the current operand stack.
664    */
665    void translateFrame(MethodVisitor methodVisitor, int frameType, int localVariableLength, Object[] localVariable, int stackSize, Object[] stack);
666   
667    /**
668    * Injects a frame indicating the beginning of a return value handler for the currently handled method.
669    *
670    * @param methodVisitor The method visitor onto which to apply the stack map frame.
671    */
672    void injectReturnFrame(MethodVisitor methodVisitor);
673   
674    /**
675    * Injects a frame indicating the beginning of an exception handler for the currently handled method.
676    *
677    * @param methodVisitor The method visitor onto which to apply the stack map frame.
678    */
679    void injectExceptionFrame(MethodVisitor methodVisitor);
680   
681    /**
682    * Injects a frame indicating the completion of the currently handled method, i.e. all yielded types were added.
683    *
684    * @param methodVisitor The method visitor onto which to apply the stack map frame.
685    * @param secondary {@code true} if another completion frame for this method was written previously.
686    */
687    void injectCompletionFrame(MethodVisitor methodVisitor, boolean secondary);
688   
689    /**
690    * A stack map frame handler for an instrumented method.
691    */
 
692    interface ForInstrumentedMethod extends StackMapFrameHandler {
693   
694    /**
695    * Binds this meta data handler for the entry advice.
696    *
697    * @param adviceMethod The entry advice method.
698    * @return An appropriate meta data handler for the enter method.
699    */
700    ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod);
701   
702    /**
703    * Binds this meta data handler for the exit advice.
704    *
705    * @param adviceMethod The exit advice method.
706    * @return An appropriate meta data handler for the enter method.
707    */
708    ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod);
709   
710    /**
711    * Returns a hint to supply to a {@link ClassReader} when parsing an advice method.
712    *
713    * @return The reader hint to supply to an ASM class reader.
714    */
715    int getReaderHint();
716    }
717   
718    /**
719    * A stack map frame handler for an advice method.
720    */
 
721    interface ForAdvice extends StackMapFrameHandler {
722    /* marker interface */
723    }
724   
725    /**
726    * A non-operational stack map frame handler.
727    */
 
728    enum NoOp implements ForInstrumentedMethod, ForAdvice {
729   
730    /**
731    * The singleton instance.
732    */
733    INSTANCE;
734   
 
735  0 toggle @Override
736    public StackMapFrameHandler.ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod) {
737  0 return this;
738    }
739   
 
740  0 toggle @Override
741    public StackMapFrameHandler.ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod) {
742  0 return this;
743    }
744   
 
745  0 toggle @Override
746    public int getReaderHint() {
747  0 return ClassReader.SKIP_FRAMES;
748    }
749   
 
750  0 toggle @Override
751    public void translateFrame(MethodVisitor methodVisitor,
752    int frameType,
753    int localVariableLength,
754    Object[] localVariable,
755    int stackSize,
756    Object[] stack) {
757    /* do nothing */
758    }
759   
760   
 
761  0 toggle @Override
762    public void injectReturnFrame(MethodVisitor methodVisitor) {
763    /* do nothing */
764    }
765   
 
766  0 toggle @Override
767    public void injectExceptionFrame(MethodVisitor methodVisitor) {
768    /* do nothing */
769    }
770   
 
771  0 toggle @Override
772    public void injectCompletionFrame(MethodVisitor methodVisitor, boolean secondary) {
773    /* do nothing */
774    }
775   
 
776  1 toggle @Override
777    public String toString() {
778  1 return "Advice.StackMapFrameHandler.NoOp." + name();
779    }
780    }
781   
782    /**
783    * A default implementation of a stack map frame handler for an instrumented method.
784    */
 
785    class Default implements ForInstrumentedMethod {
786   
787    /**
788    * An empty array indicating an empty frame.
789    */
790    private static final Object[] EMPTY = new Object[0];
791   
792    /**
793    * The instrumented method.
794    */
795    protected final MethodDescription.InDefinedShape instrumentedMethod;
796   
797    /**
798    * A list of intermediate types to be considered as part of the instrumented method's steady signature.
799    */
800    protected final TypeList requiredTypes;
801   
802    /**
803    * The types that are expected to be added after the instrumented method returns.
804    */
805    protected final TypeList yieldedTypes;
806   
807    /**
808    * {@code true} if the meta data handler is expected to expand its frames.
809    */
810    private final boolean expandFrames;
811   
812    /**
813    * The current frame's size divergence from the original local variable array.
814    */
815    private int currentFrameDivergence;
816   
817    /**
818    * Creates a new default meta data handler.
819    *
820    * @param instrumentedMethod The instrumented method.
821    * @param requiredTypes A list of intermediate types to be considered as part of the instrumented method's steady signature.
822    * @param yieldedTypes The types that are expected to be added after the instrumented method returns.
823    * @param expandFrames {@code true} if the meta data handler is expected to expand its frames.
824    */
 
825  296 toggle protected Default(MethodDescription.InDefinedShape instrumentedMethod,
826    TypeList requiredTypes,
827    TypeList yieldedTypes,
828    boolean expandFrames) {
829  296 this.instrumentedMethod = instrumentedMethod;
830  296 this.requiredTypes = requiredTypes;
831  296 this.yieldedTypes = yieldedTypes;
832  296 this.expandFrames = expandFrames;
833    }
834   
835    /**
836    * Creates an appropriate stack map frame handler for an instrumented method.
837    *
838    * @param instrumentedMethod The instrumented method.
839    * @param requiredTypes A list of intermediate types to be considered as part of the instrumented method's steady signature.
840    * @param yieldedTypes The types that are expected to be added after the instrumented method returns.
841    * @param classFileVersion The instrumented type's class file version.
842    * @param writerFlags The flags supplied to the ASM writier.
843    * @param readerFlags The reader flags supplied to the ASM reader.
844    * @return An approrpiate stack map frame handler for an instrumented method.
845    */
 
846  312 toggle protected static ForInstrumentedMethod of(MethodDescription.InDefinedShape instrumentedMethod,
847    List<? extends TypeDescription> requiredTypes,
848    List<? extends TypeDescription> yieldedTypes,
849    ClassFileVersion classFileVersion,
850    int writerFlags,
851    int readerFlags) {
852  294 return (writerFlags & ClassWriter.COMPUTE_FRAMES) != 0 || classFileVersion.isLessThan(ClassFileVersion.JAVA_V6)
853    ? NoOp.INSTANCE
854    : new Default(instrumentedMethod, new TypeList.Explicit(requiredTypes), new TypeList.Explicit(yieldedTypes), (readerFlags & ClassReader.EXPAND_FRAMES) != 0);
855    }
856   
857    /**
858    * Translates a type into a representation of its form inside a stack map frame.
859    *
860    * @param typeDescription The type to translate.
861    * @return A stack entry representation of the supplied type.
862    */
 
863  5199 toggle protected static Object toFrame(TypeDescription typeDescription) {
864  4890 if (typeDescription.represents(boolean.class)
865    || typeDescription.represents(byte.class)
866    || typeDescription.represents(short.class)
867    || typeDescription.represents(char.class)
868    || typeDescription.represents(int.class)) {
869  0 return Opcodes.INTEGER;
870  4823 } else if (typeDescription.represents(long.class)) {
871  0 return Opcodes.LONG;
872  4756 } else if (typeDescription.represents(float.class)) {
873  0 return Opcodes.FLOAT;
874  4689 } else if (typeDescription.represents(double.class)) {
875  0 return Opcodes.DOUBLE;
876    } else {
877  4689 return typeDescription.getInternalName();
878    }
879    }
880   
 
881  194 toggle @Override
882    public StackMapFrameHandler.ForAdvice bindEntry(MethodDescription.InDefinedShape adviceMethod) {
883  194 return new ForAdvice(adviceMethod, new TypeList.Empty(), requiredTypes, TranslationMode.ENTRY);
884    }
885   
 
886  243 toggle @Override
887    public StackMapFrameHandler.ForAdvice bindExit(MethodDescription.InDefinedShape adviceMethod) {
888  243 return new ForAdvice(adviceMethod, new TypeList.Explicit(CompoundList.of(requiredTypes, yieldedTypes)), new TypeList.Empty(), TranslationMode.EXIT);
889    }
890   
 
891  383 toggle @Override
892    public int getReaderHint() {
893  355 return expandFrames
894    ? ClassReader.EXPAND_FRAMES
895    : AsmVisitorWrapper.NO_FLAGS;
896    }
897   
 
898  2703 toggle @Override
899    public void translateFrame(MethodVisitor methodVisitor,
900    int type,
901    int localVariableLength,
902    Object[] localVariable,
903    int stackSize,
904    Object[] stack) {
905  2703 translateFrame(methodVisitor,
906    TranslationMode.COPY,
907    instrumentedMethod,
908    requiredTypes,
909    type,
910    localVariableLength,
911    localVariable,
912    stackSize,
913    stack);
914    }
915   
916    /**
917    * Translates a frame.
918    *
919    * @param methodVisitor The method visitor to write the frame to.
920    * @param translationMode The translation mode to apply.
921    * @param methodDescription The method description for which the frame is written.
922    * @param additionalTypes The additional types to consider part of the instrumented method's parameters.
923    * @param frameType The frame's type.
924    * @param localVariableLength The local variable length.
925    * @param localVariable An array containing the types of the current local variables.
926    * @param stackSize The size of the operand stack.
927    * @param stack An array containing the types of the current operand stack.
928    */
 
929  8806 toggle protected void translateFrame(MethodVisitor methodVisitor,
930    TranslationMode translationMode,
931    MethodDescription.InDefinedShape methodDescription,
932    TypeList additionalTypes,
933    int frameType,
934    int localVariableLength,
935    Object[] localVariable,
936    int stackSize,
937    Object[] stack) {
938  8806 switch (frameType) {
939  4162 case Opcodes.F_SAME:
940  2384 case Opcodes.F_SAME1:
941  6546 break;
942  0 case Opcodes.F_APPEND:
943  0 currentFrameDivergence += localVariableLength;
944  0 break;
945  0 case Opcodes.F_CHOP:
946  0 currentFrameDivergence -= localVariableLength;
947  0 break;
948  0 case Opcodes.F_FULL:
949  0 case Opcodes.F_NEW:
950  0 Object[] translated = new Object[localVariableLength
951    - methodDescription.getParameters().size()
952  0 - (methodDescription.isStatic() ? 0 : 1)
953    + instrumentedMethod.getParameters().size()
954  0 + (instrumentedMethod.isStatic() ? 0 : 1)
955    + additionalTypes.size()];
956  0 int index = translationMode.copy(instrumentedMethod, methodDescription, localVariable, translated);
957  0 for (TypeDescription typeDescription : additionalTypes) {
958  0 translated[index++] = toFrame(typeDescription);
959    }
960  0 System.arraycopy(localVariable,
961  0 methodDescription.getParameters().size() + (methodDescription.isStatic() ? 0 : 1),
962    translated,
963    index,
964    translated.length - index);
965  0 localVariableLength = translated.length;
966  0 localVariable = translated;
967  0 currentFrameDivergence = translated.length - index;
968  0 break;
969  0 default:
970  0 throw new IllegalArgumentException("Unexpected frame frameType: " + frameType);
971    }
972  8806 methodVisitor.visitFrame(frameType, localVariableLength, localVariable, stackSize, stack);
973    }
974   
 
975  247 toggle @Override
976    public void injectReturnFrame(MethodVisitor methodVisitor) {
977  217 if (!expandFrames && currentFrameDivergence == 0 && !instrumentedMethod.isConstructor()) {
978  217 if (instrumentedMethod.getReturnType().represents(void.class)) {
979  73 methodVisitor.visitFrame(Opcodes.F_SAME, 0, EMPTY, 0, EMPTY);
980    } else {
981  144 methodVisitor.visitFrame(Opcodes.F_SAME1, 0, EMPTY, 1, new Object[]{toFrame(instrumentedMethod.getReturnType().asErasure())});
982    }
983    } else {
984  0 injectFullFrame(methodVisitor, requiredTypes, instrumentedMethod.getReturnType().represents(void.class)
985    ? Collections.<TypeDescription>emptyList()
986    : Collections.singletonList(instrumentedMethod.getReturnType().asErasure()));
987    }
988    }
989   
 
990  0 toggle @Override
991    public void injectExceptionFrame(MethodVisitor methodVisitor) {
992  0 if (!expandFrames && currentFrameDivergence == 0) {
993  0 methodVisitor.visitFrame(Opcodes.F_SAME1, 0, EMPTY, 1, new Object[]{Type.getInternalName(Throwable.class)});
994    } else {
995  0 injectFullFrame(methodVisitor, requiredTypes, Collections.singletonList(TypeDescription.THROWABLE));
996    }
997    }
998   
 
999  292 toggle @Override
1000    public void injectCompletionFrame(MethodVisitor methodVisitor, boolean secondary) {
1001  267 if (!expandFrames && currentFrameDivergence == 0 && (secondary || !instrumentedMethod.isConstructor())) {
1002  175 if (secondary) {
1003  0 methodVisitor.visitFrame(Opcodes.F_SAME, 0, EMPTY, 0, EMPTY);
1004    } else {
1005  175 Object[] local = new Object[yieldedTypes.size()];
1006  175 int index = 0;
1007  175 for (TypeDescription typeDescription : yieldedTypes) {
1008  248 local[index++] = toFrame(typeDescription);
1009    }
1010  175 methodVisitor.visitFrame(Opcodes.F_APPEND, local.length, local, 0, EMPTY);
1011    }
1012    } else {
1013  0 injectFullFrame(methodVisitor, CompoundList.of(requiredTypes, yieldedTypes), Collections.<TypeDescription>emptyList());
1014    }
1015    }
1016   
1017    /**
1018    * Injects a full stack map frame.
1019    *
1020    * @param methodVisitor The method visitor onto which to write the stack map frame.
1021    * @param typesInArray The types that were added to the local variable array additionally to the values of the instrumented method.
1022    * @param typesOnStack The types currently on the operand stack.
1023    */
 
1024  0 toggle protected void injectFullFrame(MethodVisitor methodVisitor,
1025    List<? extends TypeDescription> typesInArray,
1026    List<? extends TypeDescription> typesOnStack) {
1027  0 Object[] localVariable = new Object[instrumentedMethod.getParameters().size()
1028  0 + (instrumentedMethod.isStatic() ? 0 : 1)
1029    + typesInArray.size()];
1030  0 int index = 0;
1031  0 if (!instrumentedMethod.isStatic()) {
1032  0 localVariable[index++] = toFrame(instrumentedMethod.getDeclaringType());
1033    }
1034  0 for (TypeDescription typeDescription : instrumentedMethod.getParameters().asTypeList().asErasures()) {
1035  0 localVariable[index++] = toFrame(typeDescription);
1036    }
1037  0 for (TypeDescription typeDescription : typesInArray) {
1038  0 localVariable[index++] = toFrame(typeDescription);
1039    }
1040  0 index = 0;
1041  0 Object[] stackType = new Object[typesOnStack.size()];
1042  0 for (TypeDescription typeDescription : typesOnStack) {
1043  0 stackType[index++] = toFrame(typeDescription);
1044    }
1045  0 methodVisitor.visitFrame(expandFrames ? Opcodes.F_NEW : Opcodes.F_FULL, localVariable.length, localVariable, stackType.length, stackType);
1046  0 currentFrameDivergence = 0;
1047    }
1048   
 
1049  7 toggle @Override
1050    public String toString() {
1051  7 return "Advice.StackMapFrameHandler.Default{" +
1052    "instrumentedMethod=" + instrumentedMethod +
1053    ", requiredTypes=" + requiredTypes +
1054    ", yieldedTypes=" + yieldedTypes +
1055    ", expandFrames=" + expandFrames +
1056    ", currentFrameDivergence=" + currentFrameDivergence +
1057    '}';
1058    }
1059   
1060    /**
1061    * A translation mode that determines how the fixed frames of the instrumented method are written.
1062    */
 
1063    protected enum TranslationMode {
1064   
1065    /**
1066    * A translation mode that simply copies the original frames which are available when translating frames of the instrumented method.
1067    */
1068    COPY {
 
1069  0 toggle @Override
1070    protected int copy(MethodDescription.InDefinedShape instrumentedMethod,
1071    MethodDescription.InDefinedShape methodDescription,
1072    Object[] localVariable,
1073    Object[] translated) {
1074  0 int length = instrumentedMethod.getParameters().size() + (instrumentedMethod.isStatic() ? 0 : 1);
1075  0 System.arraycopy(localVariable, 0, translated, 0, length);
1076  0 return length;
1077    }
1078    },
1079   
1080    /**
1081    * A translation mode for the entry advice that considers that the {@code this} reference might not be initialized for a constructor.
1082    */
1083    ENTRY {
 
1084  0 toggle @Override
1085    protected int copy(MethodDescription.InDefinedShape instrumentedMethod,
1086    MethodDescription.InDefinedShape methodDescription,
1087    Object[] localVariable,
1088    Object[] translated) {
1089  0 int index = 0;
1090  0 if (!instrumentedMethod.isStatic()) {
1091  0 translated[index++] = instrumentedMethod.isConstructor()
1092    ? Opcodes.UNINITIALIZED_THIS
1093    : toFrame(instrumentedMethod.getDeclaringType());
1094    }
1095  0 for (TypeDescription typeDescription : instrumentedMethod.getParameters().asTypeList().asErasures()) {
1096  0 translated[index++] = toFrame(typeDescription);
1097    }
1098  0 return index;
1099   
1100    }
1101    },
1102   
1103    /**
1104    * A translation mode for an exit advice where the {@code this} reference is always initialized.
1105    */
1106    EXIT {
 
1107  0 toggle @Override
1108    protected int copy(MethodDescription.InDefinedShape instrumentedMethod,
1109    MethodDescription.InDefinedShape methodDescription,
1110    Object[] localVariable,
1111    Object[] translated) {
1112  0 int index = 0;
1113  0 if (!instrumentedMethod.isStatic()) {
1114  0 translated[index++] = toFrame(instrumentedMethod.getDeclaringType());
1115    }
1116  0 for (TypeDescription typeDescription : instrumentedMethod.getParameters().asTypeList().asErasures()) {
1117  0 translated[index++] = toFrame(typeDescription);
1118    }
1119  0 return index;
1120    }
1121    };
1122   
1123    /**
1124    * Copies the fixed parameters of the instrumented method onto the operand stack.
1125    *
1126    * @param instrumentedMethod The instrumented method.
1127    * @param methodDescription The method for which a frame is created.
1128    * @param localVariable The original local variable array.
1129    * @param translated The array containing the translated frames.
1130    * @return The amount of frames added to the translated frame array.
1131    */
1132    protected abstract int copy(MethodDescription.InDefinedShape instrumentedMethod,
1133    MethodDescription.InDefinedShape methodDescription,
1134    Object[] localVariable,
1135    Object[] translated);
1136   
 
1137  9 toggle @Override
1138    public String toString() {
1139  9 return "Advice.StackMapFrameHandler.Default.TranslationMode." + name();
1140    }
1141    }
1142   
1143    /**
1144    * A stack map frame handler for an advice method.
1145    */
 
1146    protected class ForAdvice implements StackMapFrameHandler.ForAdvice {
1147   
1148    /**
1149    * The method description for which frames are translated.
1150    */
1151    protected final MethodDescription.InDefinedShape adviceMethod;
1152   
1153    /**
1154    * A list of intermediate types to be considered as part of the instrumented method's steady signature.
1155    */
1156    protected final TypeList requiredTypes;
1157   
1158    /**
1159    * The types that this method yields as a result.
1160    */
1161    private final TypeList yieldedTypes;
1162   
1163    /**
1164    * The translation mode to apply for this advice method. Should be either {@link TranslationMode#ENTRY} or {@link TranslationMode#EXIT}.
1165    */
1166    protected final TranslationMode translationMode;
1167   
1168    /**
1169    * Creates a new meta data handler for an advice method.
1170    *
1171    * @param adviceMethod The method description for which frames are translated.
1172    * @param requiredTypes A list of expected types to be considered as part of the instrumented method's steady signature.
1173    * @param yieldedTypes The types that this method yields as a result.
1174    * @param translationMode The translation mode to apply for this advice method. Should be
1175    * either {@link TranslationMode#ENTRY} or {@link TranslationMode#EXIT}.
1176    */
 
1177  439 toggle protected ForAdvice(MethodDescription.InDefinedShape adviceMethod,
1178    TypeList requiredTypes,
1179    TypeList yieldedTypes,
1180    TranslationMode translationMode) {
1181  439 this.adviceMethod = adviceMethod;
1182  439 this.requiredTypes = requiredTypes;
1183  439 this.yieldedTypes = yieldedTypes;
1184  439 this.translationMode = translationMode;
1185    }
1186   
 
1187  6103 toggle @Override
1188    public void translateFrame(MethodVisitor methodVisitor,
1189    int type,
1190    int localVariableLength,
1191    Object[] localVariable,
1192    int stackSize,
1193    Object[] stack) {
1194  6103 Default.this.translateFrame(methodVisitor,
1195    translationMode,
1196    adviceMethod,
1197    requiredTypes,
1198    type,
1199    localVariableLength,
1200    localVariable,
1201    stackSize,
1202    stack);
1203    }
1204   
 
1205  73 toggle @Override
1206    public void injectReturnFrame(MethodVisitor methodVisitor) {
1207  60 if (!expandFrames && currentFrameDivergence == 0) {
1208  60 if (yieldedTypes.isEmpty() || adviceMethod.getReturnType().represents(void.class)) {
1209  0 methodVisitor.visitFrame(Opcodes.F_SAME, 0, EMPTY, 0, EMPTY);
1210    } else {
1211  60 methodVisitor.visitFrame(Opcodes.F_SAME1, 0, EMPTY, 1, new Object[]{toFrame(adviceMethod.getReturnType().asErasure())});
1212    }
1213    } else {
1214  0 injectFullFrame(methodVisitor, requiredTypes, yieldedTypes.isEmpty() || adviceMethod.getReturnType().represents(void.class)
1215    ? Collections.<TypeDescription>emptyList()
1216    : Collections.singletonList(adviceMethod.getReturnType().asErasure()));
1217    }
1218    }
1219   
 
1220  0 toggle @Override
1221    public void injectExceptionFrame(MethodVisitor methodVisitor) {
1222  0 if (!expandFrames && currentFrameDivergence == 0) {
1223  0 methodVisitor.visitFrame(Opcodes.F_SAME1, 0, EMPTY, 1, new Object[]{Type.getInternalName(Throwable.class)});
1224    } else {
1225  0 injectFullFrame(methodVisitor, requiredTypes, Collections.singletonList(TypeDescription.THROWABLE));
1226    }
1227    }
1228   
 
1229  422 toggle @Override
1230    public void injectCompletionFrame(MethodVisitor methodVisitor, boolean secondary) {
1231  394 if ((!expandFrames && currentFrameDivergence == 0 && yieldedTypes.size() < 4)) {
1232  87 if (secondary || yieldedTypes.isEmpty()) {
1233  0 methodVisitor.visitFrame(Opcodes.F_SAME, 0, EMPTY, 0, EMPTY);
1234    } else {
1235  87 Object[] local = new Object[yieldedTypes.size()];
1236  87 int index = 0;
1237  87 for (TypeDescription typeDescription : yieldedTypes) {
1238  87 local[index++] = toFrame(typeDescription);
1239    }
1240  87 methodVisitor.visitFrame(Opcodes.F_APPEND, local.length, local, 0, EMPTY);
1241    }
1242    } else {
1243  0 injectFullFrame(methodVisitor, CompoundList.of(requiredTypes, yieldedTypes), Collections.<TypeDescription>emptyList());
1244    }
1245    }
1246   
 
1247  6 toggle @Override
1248    public String toString() {
1249  6 return "Advice.StackMapFrameHandler.Default.ForAdvice{" +
1250    "adviceMethod=" + adviceMethod +
1251    ", requiredTypes=" + requiredTypes +
1252    ", yieldedTypes=" + yieldedTypes +
1253    ", translationMode=" + translationMode +
1254    '}';
1255    }
1256    }
1257    }
1258    }
1259   
1260    /**
1261    * A method visitor that weaves the advice methods' byte codes.
1262    */
 
1263    protected abstract static class AdviceVisitor extends ExceptionTableSensitiveMethodVisitor {
1264   
1265    /**
1266    * Indicates a zero offset.
1267    */
1268    private static final int NO_OFFSET = 0;
1269   
1270    /**
1271    * A description of the instrumented method.
1272    */
1273    protected final MethodDescription.InDefinedShape instrumentedMethod;
1274   
1275    /**
1276    * The required padding before using local variables after the instrumented method's arguments.
1277    */
1278    private final int padding;
1279   
1280    /**
1281    * The dispatcher to be used for method entry.
1282    */
1283    private final Dispatcher.Bound methodEnter;
1284   
1285    /**
1286    * The dispatcher to be used for method exit.
1287    */
1288    protected final Dispatcher.Bound methodExit;
1289   
1290    /**
1291    * A handler for computing the method size requirements.
1292    */
1293    protected final MethodSizeHandler methodSizeHandler;
1294   
1295    /**
1296    * A handler for translating and injecting stack map frames.
1297    */
1298    protected final StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler;
1299   
1300    /**
1301    * Creates a new advice visitor.
1302    *
1303    * @param methodVisitor The method visitor to which all instructions are written.
1304    * @param instrumentedMethod The instrumented method.
1305    * @param methodEnter The method enter advice.
1306    * @param methodExit The method exit advice.
1307    * @param yieldedTypes The types that are expected to be added after the instrumented method returns.
1308    * @param classFileVersion The instrumented type's class file version.
1309    * @param writerFlags The ASM writer flags that were set.
1310    * @param readerFlags The ASM reader flags that were set.
1311    */
 
1312  312 toggle protected AdviceVisitor(MethodVisitor methodVisitor,
1313    MethodDescription.InDefinedShape instrumentedMethod,
1314    Dispatcher.Resolved.ForMethodEnter methodEnter,
1315    Dispatcher.Resolved.ForMethodExit methodExit,
1316    List<? extends TypeDescription> yieldedTypes,
1317    ClassFileVersion classFileVersion,
1318    int writerFlags,
1319    int readerFlags) {
1320  312 super(Opcodes.ASM5, methodVisitor);
1321  312 this.instrumentedMethod = instrumentedMethod;
1322  312 padding = methodEnter.getEnterType().getStackSize().getSize();
1323  312 List<TypeDescription> requiredTypes = methodEnter.getEnterType().represents(void.class)
1324    ? Collections.<TypeDescription>emptyList()
1325    : Collections.singletonList(methodEnter.getEnterType());
1326  312 methodSizeHandler = MethodSizeHandler.Default.of(instrumentedMethod, requiredTypes, yieldedTypes, writerFlags);
1327  312 stackMapFrameHandler = StackMapFrameHandler.Default.of(instrumentedMethod, requiredTypes, yieldedTypes, classFileVersion, writerFlags, readerFlags);
1328  312 this.methodEnter = methodEnter.bind(instrumentedMethod, methodVisitor, methodSizeHandler, stackMapFrameHandler);
1329  311 this.methodExit = methodExit.bind(instrumentedMethod, methodVisitor, methodSizeHandler, stackMapFrameHandler);
1330    }
1331   
 
1332  311 toggle @Override
1333    protected void onAfterExceptionTable() {
1334  311 methodEnter.prepare();
1335  311 onUserPrepare();
1336  311 methodExit.prepare();
1337  311 methodEnter.apply();
1338  286 onUserStart();
1339    }
1340   
1341    /**
1342    * Invoked when the user method's exception handler (if any) is supposed to be prepared.
1343    */
1344    protected abstract void onUserPrepare();
1345   
1346    /**
1347    * Writes the advice for entering the instrumented method.
1348    */
1349    protected abstract void onUserStart();
1350   
 
1351  2747 toggle @Override
1352    protected void onVisitVarInsn(int opcode, int offset) {
1353  2747 mv.visitVarInsn(opcode, offset < instrumentedMethod.getStackSize()
1354    ? offset
1355    : padding + offset);
1356    }
1357   
 
1358  0 toggle @Override
1359    protected void onVisitIincInsn(int offset, int increment) {
1360  0 mv.visitIincInsn(offset < instrumentedMethod.getStackSize()
1361    ? offset
1362    : padding + offset, increment);
1363    }
1364   
1365    /**
1366    * Access the first variable after the instrumented variables and return type are stored.
1367    *
1368    * @param opcode The opcode for accessing the variable.
1369    */
 
1370  458 toggle protected void variable(int opcode) {
1371  458 variable(opcode, NO_OFFSET);
1372    }
1373   
1374    /**
1375    * Access the first variable after the instrumented variables and return type are stored.
1376    *
1377    * @param opcode The opcode for accessing the variable.
1378    * @param offset The additional offset of the variable.
1379    */
 
1380  906 toggle protected void variable(int opcode, int offset) {
1381  906 mv.visitVarInsn(opcode, instrumentedMethod.getStackSize() + padding + offset);
1382    }
1383   
 
1384  3477 toggle @Override
1385    public void visitFrame(int frameType, int localVariableLength, Object[] localVariable, int stackSize, Object[] stack) {
1386  3477 stackMapFrameHandler.translateFrame(mv, frameType, localVariableLength, localVariable, stackSize, stack);
1387    }
1388   
 
1389  286 toggle @Override
1390    public void visitMaxs(int stackSize, int localVariableLength) {
1391  286 onUserEnd();
1392  0 mv.visitMaxs(methodSizeHandler.compoundStackSize(stackSize), methodSizeHandler.compoundLocalVariableLength(localVariableLength));
1393    }
1394   
1395    /**
1396    * Writes the advice for completing the instrumented method.
1397    */
1398    protected abstract void onUserEnd();
1399   
1400    /**
1401    * An advice visitor that does not apply exit advice.
1402    */
 
1403    protected static class WithoutExitAdvice extends AdviceVisitor {
1404   
1405    /**
1406    * Creates an advice visitor that does not apply exit advice.
1407    *
1408    * @param methodVisitor The method visitor for the instrumented method.
1409    * @param instrumentedMethod A description of the instrumented method.
1410    * @param methodEnter The dispatcher to be used for method entry.
1411    * @param classFileVersion The instrumented type's class file version.
1412    * @param writerFlags The ASM writer flags that were set.
1413    * @param readerFlags The ASM reader flags that were set.
1414    */
 
1415  44 toggle protected WithoutExitAdvice(MethodVisitor methodVisitor,
1416    MethodDescription.InDefinedShape instrumentedMethod,
1417    Dispatcher.Resolved.ForMethodEnter methodEnter,
1418    ClassFileVersion classFileVersion,
1419    int writerFlags,
1420    int readerFlags) {
1421  44 super(methodVisitor,
1422    instrumentedMethod,
1423    methodEnter,
1424    Dispatcher.Inactive.INSTANCE,
1425    Collections.<TypeDescription>emptyList(),
1426    classFileVersion,
1427    writerFlags,
1428    readerFlags);
1429    }
1430   
 
1431  44 toggle @Override
1432    protected void onUserPrepare() {
1433    /* do nothing */
1434    }
1435   
 
1436  0 toggle @Override
1437    protected void onUserStart() {
1438    /* do nothing */
1439    }
1440   
 
1441  0 toggle @Override
1442    protected void onUserEnd() {
1443    /* do nothing */
1444    }
1445   
 
1446  0 toggle @Override
1447    public String toString() {
1448  0 return "Advice.AdviceVisitor.WithoutExitAdvice{" +
1449    ", instrumentedMethod=" + instrumentedMethod +
1450    "}";
1451    }
1452    }
1453   
1454    /**
1455    * An advice visitor that applies exit advice.
1456    */
 
1457    protected abstract static class WithExitAdvice extends AdviceVisitor {
1458   
1459    /**
1460    * Indicates the handler for the value returned by the advice method.
1461    */
1462    protected final Label returnHandler;
1463   
1464    /**
1465    * Creates an advice visitor that applies exit advice.
1466    *
1467    * @param methodVisitor The method visitor for the instrumented method.
1468    * @param instrumentedMethod A description of the instrumented method.
1469    * @param methodEnter The dispatcher to be used for method entry.
1470    * @param methodExit The dispatcher to be used for method exit.
1471    * @param yieldedTypes The types that are expected to be added after the instrumented method returns.
1472    * @param classFileVersion The instrumented type's class file version.
1473    * @param writerFlags The ASM writer flags that were set.
1474    * @param readerFlags The ASM reader flags that were set.
1475    */
 
1476  267 toggle protected WithExitAdvice(MethodVisitor methodVisitor,
1477    MethodDescription.InDefinedShape instrumentedMethod,
1478    Dispatcher.Resolved.ForMethodEnter methodEnter,
1479    Dispatcher.Resolved.ForMethodExit methodExit,
1480    List<? extends TypeDescription> yieldedTypes,
1481    ClassFileVersion classFileVersion,
1482    int writerFlags,
1483    int readerFlags) {
1484  267 super(methodVisitor, instrumentedMethod, methodEnter, methodExit, yieldedTypes, classFileVersion, writerFlags, readerFlags);
1485  267 returnHandler = new Label();
1486    }
1487   
 
1488  4685 toggle @Override
1489    protected void onVisitInsn(int opcode) {
1490  4685 switch (opcode) {
1491  70 case Opcodes.RETURN:
1492  0 case Opcodes.IRETURN:
1493  0 case Opcodes.FRETURN:
1494  0 case Opcodes.DRETURN:
1495  0 case Opcodes.LRETURN:
1496  134 case Opcodes.ARETURN:
1497  230 mv.visitJumpInsn(Opcodes.GOTO, returnHandler);
1498  230 break;
1499  4455 default:
1500  4455 mv.visitInsn(opcode);
1501    }
1502    }
1503   
 
1504  261 toggle @Override
1505    protected void onUserEnd() {
1506  261 mv.visitLabel(returnHandler);
1507  261 stackMapFrameHandler.injectReturnFrame(mv);
1508  261 Type returnType = Type.getType(instrumentedMethod.getReturnType().asErasure().getDescriptor());
1509  261 if (!returnType.equals(Type.VOID_TYPE)) {
1510  185 variable(returnType.getOpcode(Opcodes.ISTORE));
1511    }
1512  261 onUserReturn();
1513  261 methodExit.apply();
1514  0 onExitAdviceReturn();
1515  0 if (returnType.equals(Type.VOID_TYPE)) {
1516  0 mv.visitInsn(Opcodes.RETURN);
1517    } else {
1518  0 variable(returnType.getOpcode(Opcodes.ILOAD));
1519  0 mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1520    }
1521    }
1522   
1523    /**
1524    * Invoked after the user method has returned.
1525    */
1526    protected abstract void onUserReturn();
1527   
1528    /**
1529    * Invoked after the exit advice method has returned.
1530    */
1531    protected abstract void onExitAdviceReturn();
1532   
1533    /**
1534    * An advice visitor that does not capture exceptions.
1535    */
 
1536    protected static class WithoutExceptionHandling extends WithExitAdvice {
1537   
1538    /**
1539    * Creates a new advice visitor that does not capture exceptions.
1540    *
1541    * @param methodVisitor The method visitor for the instrumented method.
1542    * @param instrumentedMethod A description of the instrumented method.
1543    * @param methodEnter The dispatcher to be used for method entry.
1544    * @param methodExit The dispatcher to be used for method exit.
1545    * @param classFileVersion The instrumented type's class file version.
1546    * @param writerFlags The ASM writer flags that were set.
1547    * @param readerFlags The ASM reader flags that were set.
1548    */
 
1549  155 toggle protected WithoutExceptionHandling(MethodVisitor methodVisitor,
1550    MethodDescription.InDefinedShape instrumentedMethod,
1551    Dispatcher.Resolved.ForMethodEnter methodEnter,
1552    Dispatcher.Resolved.ForMethodExit methodExit,
1553    ClassFileVersion classFileVersion,
1554    int writerFlags,
1555    int readerFlags) {
1556  155 super(methodVisitor,
1557    instrumentedMethod,
1558    methodEnter,
1559    methodExit,
1560  155 instrumentedMethod.getReturnType().represents(void.class)
1561    ? Collections.<TypeDescription>emptyList()
1562    : Collections.singletonList(instrumentedMethod.getReturnType().asErasure()),
1563    classFileVersion,
1564    writerFlags,
1565    readerFlags);
1566    }
1567   
 
1568  155 toggle @Override
1569    protected void onUserPrepare() {
1570    /* empty */
1571    }
1572   
 
1573  149 toggle @Override
1574    protected void onUserStart() {
1575    /* empty */
1576    }
1577   
 
1578  149 toggle @Override
1579    protected void onUserReturn() {
1580  149 if (!instrumentedMethod.getReturnType().represents(void.class)) {
1581  92 stackMapFrameHandler.injectCompletionFrame(mv, false);
1582    }
1583    }
1584   
 
1585  0 toggle @Override
1586    protected void onExitAdviceReturn() {
1587    /* empty */
1588    }
1589   
 
1590  0 toggle @Override
1591    public String toString() {
1592  0 return "Advice.AdviceVisitor.WithExitAdvice.WithoutExceptionHandling{" +
1593    "instrumentedMethod=" + instrumentedMethod +
1594    "}";
1595    }
1596    }
1597   
1598    /**
1599    * An advice visitor that captures exceptions by weaving try-catch blocks around user code.
1600    */
 
1601    protected static class WithExceptionHandling extends WithExitAdvice {
1602   
1603    /**
1604    * The type of the handled throwable type for which this advice is invoked.
1605    */
1606    private final TypeDescription triggeringThrowable;
1607   
1608    /**
1609    * Indicates the start of the user method.
1610    */
1611    private final Label userStart;
1612   
1613    /**
1614    * Indicates the exception handler.
1615    */
1616    private final Label exceptionHandler;
1617   
1618    /**
1619    * Creates a new advice visitor that captures exception by weaving try-catch blocks around user code.
1620    *
1621    * @param methodVisitor The method visitor for the instrumented method.
1622    * @param instrumentedMethod A description of the instrumented method.
1623    * @param methodEnter The dispatcher to be used for method entry.
1624    * @param methodExit The dispatcher to be used for method exit.
1625    * @param classFileVersion The instrumented type's class file version.
1626    * @param writerFlags The ASM writer flags that were set.
1627    * @param readerFlags The ASM reader flags that were set.
1628    * @param triggeringThrowable The type of the handled throwable type for which this advice is invoked.
1629    */
 
1630  0 toggle protected WithExceptionHandling(MethodVisitor methodVisitor,
1631    MethodDescription.InDefinedShape instrumentedMethod,
1632    Dispatcher.Resolved.ForMethodEnter methodEnter,
1633    Dispatcher.Resolved.ForMethodExit methodExit,
1634    ClassFileVersion classFileVersion,
1635    int writerFlags,
1636    int readerFlags,
1637    TypeDescription triggeringThrowable) {
1638  0 super(methodVisitor,
1639    instrumentedMethod,
1640    methodEnter,
1641    methodExit,
1642  0 instrumentedMethod.getReturnType().represents(void.class)
1643    ? Collections.singletonList(TypeDescription.THROWABLE)
1644    : Arrays.asList(instrumentedMethod.getReturnType().asErasure(), TypeDescription.THROWABLE),
1645    classFileVersion,
1646    writerFlags,
1647    readerFlags);
1648  0 this.triggeringThrowable = triggeringThrowable;
1649  0 userStart = new Label();
1650  0 exceptionHandler = new Label();
1651    }
1652   
 
1653  0 toggle @Override
1654    protected void onUserPrepare() {
1655  0 mv.visitTryCatchBlock(userStart, returnHandler, exceptionHandler, triggeringThrowable.getInternalName());
1656    }
1657   
 
1658  0 toggle @Override
1659    protected void onUserStart() {
1660  0 mv.visitLabel(userStart);
1661    }
1662   
 
1663  0 toggle @Override
1664    protected void onUserReturn() {
1665  0 mv.visitInsn(Opcodes.ACONST_NULL);
1666  0 variable(Opcodes.ASTORE, instrumentedMethod.getReturnType().getStackSize().getSize());
1667  0 Label endOfHandler = new Label();
1668  0 mv.visitJumpInsn(Opcodes.GOTO, endOfHandler);
1669  0 mv.visitLabel(exceptionHandler);
1670  0 stackMapFrameHandler.injectExceptionFrame(mv);
1671  0 variable(Opcodes.ASTORE, instrumentedMethod.getReturnType().getStackSize().getSize());
1672  0 storeDefaultReturn();
1673  0 mv.visitLabel(endOfHandler);
1674  0 stackMapFrameHandler.injectCompletionFrame(mv, false);
1675    }
1676   
 
1677  0 toggle @Override
1678    protected void onExitAdviceReturn() {
1679  0 variable(Opcodes.ALOAD, instrumentedMethod.getReturnType().getStackSize().getSize());
1680  0 Label endOfHandler = new Label();
1681  0 mv.visitJumpInsn(Opcodes.IFNULL, endOfHandler);
1682  0 variable(Opcodes.ALOAD, instrumentedMethod.getReturnType().getStackSize().getSize());
1683  0 mv.visitInsn(Opcodes.ATHROW);
1684  0 mv.visitLabel(endOfHandler);
1685  0 stackMapFrameHandler.injectCompletionFrame(mv, true);
1686    }
1687   
1688    /**
1689    * Stores a default return value in the designated slot of the local variable array.
1690    */
 
1691  0 toggle private void storeDefaultReturn() {
1692  0 if (instrumentedMethod.getReturnType().represents(boolean.class)
1693    || instrumentedMethod.getReturnType().represents(byte.class)
1694    || instrumentedMethod.getReturnType().represents(short.class)
1695    || instrumentedMethod.getReturnType().represents(char.class)
1696    || instrumentedMethod.getReturnType().represents(int.class)) {
1697  0 mv.visitInsn(Opcodes.ICONST_0);
1698  0 variable(Opcodes.ISTORE);
1699  0 } else if (instrumentedMethod.getReturnType().represents(long.class)) {
1700  0 mv.visitInsn(Opcodes.LCONST_0);
1701  0 variable(Opcodes.LSTORE);
1702  0 } else if (instrumentedMethod.getReturnType().represents(float.class)) {
1703  0 mv.visitInsn(Opcodes.FCONST_0);
1704  0 variable(Opcodes.FSTORE);
1705  0 } else if (instrumentedMethod.getReturnType().represents(double.class)) {
1706  0 mv.visitInsn(Opcodes.DCONST_0);
1707  0 variable(Opcodes.DSTORE);
1708  0 } else if (!instrumentedMethod.getReturnType().represents(void.class)) {
1709  0 mv.visitInsn(Opcodes.ACONST_NULL);
1710  0 variable(Opcodes.ASTORE);
1711    }
1712    }
1713   
 
1714  0 toggle @Override
1715    public String toString() {
1716  0 return "Advice.AdviceVisitor.WithExitAdvice.WithExceptionHandling{" +
1717    "instrumentedMethod=" + instrumentedMethod +
1718    ", triggeringThrowable=" + triggeringThrowable +
1719    "}";
1720    }
1721    }
1722    }
1723    }
1724   
1725    /**
1726    * A dispatcher for implementing advice.
1727    */
 
1728    protected interface Dispatcher {
1729   
1730    /**
1731    * Indicates that a method does not represent advice and does not need to be visited.
1732    */
1733    MethodVisitor IGNORE_METHOD = null;
1734   
1735    /**
1736    * Expresses that an annotation should not be visited.
1737    */
1738    AnnotationVisitor IGNORE_ANNOTATION = null;
1739   
1740    /**
1741    * Returns {@code true} if this dispatcher is alive.
1742    *
1743    * @return {@code true} if this dispatcher is alive.
1744    */
1745    boolean isAlive();
1746   
1747    /**
1748    * A dispatcher that is not yet resolved.
1749    */
 
1750    interface Unresolved extends Dispatcher {
1751   
1752    /**
1753    * Indicates that this dispatcher requires access to the class file declaring the advice method.
1754    *
1755    * @return {@code true} if this dispatcher requires access to the advice method's class file.
1756    */
1757    boolean isBinary();
1758   
1759    /**
1760    * Resolves this dispatcher as a dispatcher for entering a method.
1761    *
1762    * @param userFactories A list of custom factories for binding parameters of an advice method.
1763    * @param binaryRepresentation An unresolved binary representation of the type containing the advice method.
1764    * @return This dispatcher as a dispatcher for entering a method.
1765    */
1766    Resolved.ForMethodEnter asMethodEnter(List<? extends OffsetMapping.Factory> userFactories,
1767    ClassFileLocator.Resolution binaryRepresentation);
1768   
1769    /**
1770    * Resolves this dispatcher as a dispatcher for exiting a method.
1771    *
1772    * @param userFactories A list of custom factories for binding parameters of an advice method.
1773    * @param binaryRepresentation An unresolved binary representation of the type containing the advice method.
1774    * @param dispatcher The dispatcher for entering a method.
1775    * @return This dispatcher as a dispatcher for exiting a method.
1776    */
1777    Resolved.ForMethodExit asMethodExitTo(List<? extends OffsetMapping.Factory> userFactories,
1778    ClassFileLocator.Resolution binaryRepresentation,
1779    Resolved.ForMethodEnter dispatcher);
1780    }
1781   
1782    /**
1783    * Represents an offset mapping for an advice method to an alternative offset.
1784    */
 
1785    interface OffsetMapping {
1786   
1787    /**
1788    * Resolves an offset mapping to a given target offset.
1789    *
1790    * @param instrumentedMethod The instrumented method for which the mapping is to be resolved.
1791    * @param context The context in which the offset mapping is applied.
1792    * @return A suitable target mapping.
1793    */
1794    Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context);
1795   
1796    /**
1797    * A context for applying an {@link OffsetMapping}.
1798    */
 
1799    interface Context {
1800   
1801    /**
1802    * Returns {@code true} if the advice is applied on a fully initialized instance, i.e. describes if the {@code this}
1803    * instance is available or still uninitialized during calling the advice.
1804    *
1805    * @return {@code true} if the advice is applied onto a fully initialized method.
1806    */
1807    boolean isInitialized();
1808   
1809    /**
1810    * Returns the padding before writing additional values that this context applies.
1811    *
1812    * @return The required padding for this context.
1813    */
1814    int getPadding();
1815   
1816    /**
1817    * A context for an offset mapping describing a method entry.
1818    */
 
1819    enum ForMethodEntry implements Context {
1820   
1821    /**
1822    * Describes a context for a method entry that is not a constructor.
1823    */
1824    INITIALIZED(true),
1825   
1826    /**
1827    * Describes a context for a method entry that is a constructor.
1828    */
1829    NON_INITIALIZED(false);
1830   
1831    /**
1832    * Resolves an appropriate method entry context for the supplied instrumented method.
1833    *
1834    * @param instrumentedMethod The instrumented method.
1835    * @return An appropriate context.
1836    */
 
1837  402 toggle protected static Context of(MethodDescription.InDefinedShape instrumentedMethod) {
1838  402 return instrumentedMethod.isConstructor()
1839    ? NON_INITIALIZED
1840    : INITIALIZED;
1841    }
1842   
1843    /**
1844    * {@code true} if the method is no constructor, i.e. is invoked for an initialized instance upon entry.
1845    */
1846    private final boolean initialized;
1847   
1848    /**
1849    * Creates a new context for a method entry.
1850    *
1851    * @param initialized {@code true} if the method is no constructor, i.e. is invoked for an initialized instance upon entry.
1852    */
 
1853  0 toggle ForMethodEntry(boolean initialized) {
1854  0 this.initialized = initialized;
1855    }
1856   
 
1857  164 toggle @Override
1858    public boolean isInitialized() {
1859  164 return initialized;
1860    }
1861   
 
1862  0 toggle @Override
1863    public int getPadding() {
1864  0 return StackSize.ZERO.getSize();
1865    }
1866   
 
1867  2 toggle @Override
1868    public String toString() {
1869  2 return "Advice.Dispatcher.OffsetMapping.Context.ForMethodEntry." + name();
1870    }
1871    }
1872   
1873    /**
1874    * A context for an offset mapping describing a method exit.
1875    */
 
1876    enum ForMethodExit implements Context {
1877   
1878    /**
1879    * A method exit with a zero sized padding.
1880    */
1881    ZERO(StackSize.ZERO),
1882   
1883    /**
1884    * A method exit with a single slot padding.
1885    */
1886    SINGLE(StackSize.SINGLE),
1887   
1888    /**
1889    * A method exit with a double slot padding.
1890    */
1891    DOUBLE(StackSize.DOUBLE);
1892   
1893    /**
1894    * The padding implied by this method exit.
1895    */
1896    private final StackSize stackSize;
1897   
1898    /**
1899    * Creates a new context for a method exit.
1900    *
1901    * @param stackSize The padding implied by this method exit.
1902    */
 
1903  0 toggle ForMethodExit(StackSize stackSize) {
1904  0 this.stackSize = stackSize;
1905    }
1906   
1907    /**
1908    * Resolves an appropriate method exit context for the supplied entry method type.
1909    *
1910    * @param typeDescription The type that is returned by the enter method.
1911    * @return An appropriate context for the supplied entry method type.
1912    */
 
1913  578 toggle protected static Context of(TypeDescription typeDescription) {
1914  578 switch (typeDescription.getStackSize()) {
1915  123 case ZERO:
1916  123 return ZERO;
1917  367 case SINGLE:
1918  367 return SINGLE;
1919  0 case DOUBLE:
1920  0 return DOUBLE;
1921  0 default:
1922  0 throw new IllegalStateException("Unknown stack size: " + typeDescription);
1923    }
1924    }
1925   
 
1926  0 toggle @Override
1927    public boolean isInitialized() {
1928  0 return true;
1929    }
1930   
 
1931  153 toggle @Override
1932    public int getPadding() {
1933  153 return stackSize.getSize();
1934    }
1935   
 
1936  3 toggle @Override
1937    public String toString() {
1938  3 return "Advice.Dispatcher.OffsetMapping.Context.ForMethodExit." + name();
1939    }
1940    }
1941    }
1942   
1943    /**
1944    * A target offset of an offset mapping.
1945    */
 
1946    interface Target {
1947   
1948    /**
1949    * Indicates that applying this target does not require any additional padding.
1950    */
1951    int NO_PADDING = 0;
1952   
1953    /**
1954    * Applies this offset mapping for a {@link MethodVisitor#visitVarInsn(int, int)} instruction.
1955    *
1956    * @param methodVisitor The method visitor onto which this offset mapping is to be applied.
1957    * @param opcode The opcode of the original instruction.
1958    * @return The required padding to the advice's total stack size.
1959    */
1960    int resolveAccess(MethodVisitor methodVisitor, int opcode);
1961   
1962    /**
1963    * Applies this offset mapping for a {@link MethodVisitor#visitIincInsn(int, int)} instruction.
1964    *
1965    * @param methodVisitor The method visitor onto which this offset mapping is to be applied.
1966    * @param increment The value with which to increment the targeted value.
1967    * @return The required padding to the advice's total stack size.
1968    */
1969    int resolveIncrement(MethodVisitor methodVisitor, int increment);
1970   
1971    /**
1972    * Loads a default value onto the stack or pops the accessed value off it.
1973    */
 
1974    enum ForDefaultValue implements Target {
1975   
1976    /**
1977    * The singleton instance.
1978    */
1979    INSTANCE;
1980   
 
1981  0 toggle @Override
1982    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
1983  0 switch (opcode) {
1984  0 case Opcodes.ALOAD:
1985  0 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
1986  0 break;
1987  0 case Opcodes.ILOAD:
1988  0 methodVisitor.visitInsn(Opcodes.ICONST_0);
1989  0 break;
1990  0 case Opcodes.LLOAD:
1991  0 methodVisitor.visitInsn(Opcodes.LCONST_0);
1992  0 break;
1993  0 case Opcodes.FLOAD:
1994  0 methodVisitor.visitInsn(Opcodes.FCONST_0);
1995  0 break;
1996  0 case Opcodes.DLOAD:
1997  0 methodVisitor.visitInsn(Opcodes.DCONST_0);
1998  0 break;
1999  0 case Opcodes.ISTORE:
2000  0 case Opcodes.FSTORE:
2001  0 case Opcodes.ASTORE:
2002  0 methodVisitor.visitInsn(Opcodes.POP);
2003  0 break;
2004  0 case Opcodes.LSTORE:
2005  0 case Opcodes.DSTORE:
2006  0 methodVisitor.visitInsn(Opcodes.POP2);
2007  0 break;
2008  0 default:
2009  0 throw new IllegalStateException("Unexpected opcode: " + opcode);
2010    }
2011  0 return NO_PADDING;
2012    }
2013   
 
2014  0 toggle @Override
2015    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2016  0 return NO_PADDING;
2017    }
2018   
 
2019  1 toggle @Override
2020    public String toString() {
2021  1 return "Advice.Dispatcher.OffsetMapping.Target.ForDefaultValue." + name();
2022    }
2023    }
2024   
2025    /**
2026    * A read-only target mapping.
2027    */
 
2028    abstract class ForParameter implements Target {
2029   
2030    /**
2031    * The mapped offset.
2032    */
2033    protected final int offset;
2034   
2035    /**
2036    * Creates a new read-only target mapping.
2037    *
2038    * @param offset The mapped offset.
2039    */
 
2040  407 toggle protected ForParameter(int offset) {
2041  407 this.offset = offset;
2042    }
2043   
 
2044  457 toggle @Override
2045    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2046  457 switch (opcode) {
2047  15 case Opcodes.ISTORE:
2048  3 case Opcodes.LSTORE:
2049  3 case Opcodes.FSTORE:
2050  3 case Opcodes.DSTORE:
2051  37 case Opcodes.ASTORE:
2052  61 onWrite(methodVisitor, opcode);
2053  0 break;
2054  0 case Opcodes.ILOAD:
2055  0 case Opcodes.LLOAD:
2056  0 case Opcodes.FLOAD:
2057  0 case Opcodes.DLOAD:
2058  0 case Opcodes.ALOAD:
2059  0 methodVisitor.visitVarInsn(opcode, offset);
2060  0 break;
2061  0 default:
2062  0 throw new IllegalArgumentException("Did not expect opcode: " + opcode);
2063    }
2064  0 return NO_PADDING;
2065    }
2066   
2067    /**
2068    * Invoked upon attempting to write to a parameter.
2069    *
2070    * @param methodVisitor The method visitor onto which this offset mapping is to be applied.
2071    * @param opcode The applied opcode.
2072    */
2073    protected abstract void onWrite(MethodVisitor methodVisitor, int opcode);
2074   
 
2075  13 toggle @Override
2076    public boolean equals(Object object) {
2077  2 if (this == object) return true;
2078  4 if (object == null || getClass() != object.getClass()) return false;
2079  7 ForParameter forParameter = (ForParameter) object;
2080  7 return offset == forParameter.offset;
2081    }
2082   
 
2083  10 toggle @Override
2084    public int hashCode() {
2085  10 return offset;
2086    }
2087   
2088    /**
2089    * A read-only parameter mapping.
2090    */
 
2091    protected static class ReadOnly extends ForParameter {
2092   
2093    /**
2094    * Creates a new parameter mapping that is only readable.
2095    *
2096    * @param offset The mapped offset.
2097    */
 
2098  330 toggle protected ReadOnly(int offset) {
2099  330 super(offset);
2100    }
2101   
 
2102  13 toggle @Override
2103    protected void onWrite(MethodVisitor methodVisitor, int opcode) {
2104  13 throw new IllegalStateException("Cannot write to read-only value");
2105    }
2106   
 
2107  0 toggle @Override
2108    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2109  0 throw new IllegalStateException("Cannot write to read-only parameter at offset " + offset);
2110    }
2111   
 
2112  2 toggle @Override
2113    public String toString() {
2114  2 return "Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadOnly{" +
2115    "offset=" + offset +
2116    "}";
2117    }
2118    }
2119   
2120    /**
2121    * A parameter mapping that is both readable and writable.
2122    */
 
2123    protected static class ReadWrite extends ForParameter {
2124   
2125    /**
2126    * Creates a new parameter mapping that is both readable and writable.
2127    *
2128    * @param offset The mapped offset.
2129    */
 
2130  77 toggle protected ReadWrite(int offset) {
2131  77 super(offset);
2132    }
2133   
 
2134  0 toggle @Override
2135    protected void onWrite(MethodVisitor methodVisitor, int opcode) {
2136  0 methodVisitor.visitVarInsn(opcode, offset);
2137    }
2138   
2139    /**
2140    * Resolves a parameter mapping where the value is casted to the given type prior to assignment.
2141    *
2142    * @param targetType The type to which the target value is cased.
2143    * @return An appropriate target mapping.
2144    */
 
2145  0 toggle protected Target casted(TypeDescription targetType) {
2146  0 return targetType.represents(Object.class)
2147    ? this
2148    : new WithCasting(offset, targetType);
2149    }
2150   
 
2151  0 toggle @Override
2152    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2153  0 methodVisitor.visitIincInsn(offset, increment);
2154  0 return NO_PADDING;
2155    }
2156   
 
2157  2 toggle @Override
2158    public String toString() {
2159  2 return "Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadWrite{" +
2160    "offset=" + offset +
2161    "}";
2162    }
2163   
2164    /**
2165    * A readable and writable parameter mapping where the assigned value is casted to another type prior to assignment.
2166    */
 
2167    protected static class WithCasting extends ReadWrite {
2168   
2169    /**
2170    * The type to which the written value is casted prior to assignment.
2171    */
2172    private final TypeDescription targetType;
2173   
2174    /**
2175    * Creates a new parameter mapping with casting prior to assignment.
2176    *
2177    * @param offset The mapped offset.
2178    * @param targetType The type to which the written value is casted prior to assignment.
2179    */
 
2180  6 toggle protected WithCasting(int offset, TypeDescription targetType) {
2181  6 super(offset);
2182  6 this.targetType = targetType;
2183    }
2184   
 
2185  0 toggle @Override
2186    protected void onWrite(MethodVisitor methodVisitor, int opcode) {
2187  0 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, targetType.getInternalName());
2188  0 super.onWrite(methodVisitor, opcode);
2189    }
2190   
 
2191  6 toggle @Override
2192    public boolean equals(Object object) {
2193  1 if (this == object) return true;
2194  2 if (object == null || getClass() != object.getClass()) return false;
2195  1 if (!super.equals(object)) return false;
2196  2 WithCasting that = (WithCasting) object;
2197  2 return targetType.equals(that.targetType);
2198    }
2199   
 
2200  4 toggle @Override
2201    public int hashCode() {
2202  4 int result = super.hashCode();
2203  4 result = 31 * result + targetType.hashCode();
2204  4 return result;
2205    }
2206   
 
2207  5 toggle @Override
2208    public String toString() {
2209  5 return "Advice.Dispatcher.OffsetMapping.Target.ForParameter.ReadWrite.WithCasting{" +
2210    "offset=" + offset +
2211    ", targetType=" + targetType +
2212    "}";
2213    }
2214    }
2215    }
2216    }
2217   
2218    /**
2219    * An offset mapping for a field.
2220    */
 
2221    abstract class ForField implements Target {
2222   
2223    /**
2224    * The field being read.
2225    */
2226    protected final FieldDescription.InDefinedShape fieldDescription;
2227   
2228    /**
2229    * Creates a new offset mapping for a field.
2230    *
2231    * @param fieldDescription The field being read.
2232    */
 
2233  227 toggle protected ForField(FieldDescription.InDefinedShape fieldDescription) {
2234  227 this.fieldDescription = fieldDescription;
2235    }
2236   
 
2237  257 toggle @Override
2238    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2239  257 switch (opcode) {
2240  0 case Opcodes.ISTORE:
2241  5 case Opcodes.ASTORE:
2242  0 case Opcodes.FSTORE:
2243  29 return onWriteSingle(methodVisitor);
2244  0 case Opcodes.LSTORE:
2245  0 case Opcodes.DSTORE:
2246  0 return onWriteDouble(methodVisitor);
2247  0 case Opcodes.ILOAD:
2248  0 case Opcodes.FLOAD:
2249  0 case Opcodes.ALOAD:
2250  0 case Opcodes.LLOAD:
2251  0 case Opcodes.DLOAD:
2252  0 if (fieldDescription.isStatic()) {
2253  0 accessField(methodVisitor, Opcodes.GETSTATIC);
2254    } else {
2255  0 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
2256  0 accessField(methodVisitor, Opcodes.GETFIELD);
2257    }
2258  0 return NO_PADDING;
2259  0 default:
2260  0 throw new IllegalArgumentException("Did not expect opcode: " + opcode);
2261    }
2262    }
2263   
2264    /**
2265    * Writes a value to a field which type occupies a single slot on the operand stack.
2266    *
2267    * @param methodVisitor The method visitor onto which this offset mapping is to be applied.
2268    * @return The required padding to the advice's total stack size.
2269    */
2270    protected abstract int onWriteSingle(MethodVisitor methodVisitor);
2271   
2272    /**
2273    * Writes a value to a field which type occupies two slots on the operand stack.
2274    *
2275    * @param methodVisitor The method visitor onto which this offset mapping is to be applied.
2276    * @return The required padding to the advice's total stack size.
2277    */
2278    protected abstract int onWriteDouble(MethodVisitor methodVisitor);
2279   
2280    /**
2281    * Accesses a field.
2282    *
2283    * @param methodVisitor The method visitor for which to access the field.
2284    * @param opcode The opcode for accessing the field.
2285    */
 
2286  0 toggle protected void accessField(MethodVisitor methodVisitor, int opcode) {
2287  0 methodVisitor.visitFieldInsn(opcode,
2288    fieldDescription.getDeclaringType().asErasure().getInternalName(),
2289    fieldDescription.getInternalName(),
2290    fieldDescription.getDescriptor());
2291    }
2292   
 
2293  10 toggle @Override
2294    public boolean equals(Object object) {
2295  2 if (this == object) return true;
2296  4 if (object == null || getClass() != object.getClass()) return false;
2297  4 ForField forField = (ForField) object;
2298  4 return fieldDescription.equals(forField.fieldDescription);
2299    }
2300   
 
2301  6 toggle @Override
2302    public int hashCode() {
2303  6 return fieldDescription.hashCode();
2304    }
2305   
2306    /**
2307    * A target mapping for a field that is only readable.
2308    */
 
2309    protected static class ReadOnly extends ForField {
2310   
2311    /**
2312    * Creates a new field mapping for a field that is only readable.
2313    *
2314    * @param fieldDescription The field which is mapped by this target mapping.
2315    */
 
2316  152 toggle protected ReadOnly(FieldDescription.InDefinedShape fieldDescription) {
2317  152 super(fieldDescription);
2318    }
2319   
 
2320  1 toggle @Override
2321    protected int onWriteSingle(MethodVisitor methodVisitor) {
2322  1 throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
2323    }
2324   
 
2325  0 toggle @Override
2326    protected int onWriteDouble(MethodVisitor methodVisitor) {
2327  0 throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
2328    }
2329   
 
2330  0 toggle @Override
2331    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2332  0 throw new IllegalStateException("Cannot write to read-only field " + fieldDescription);
2333    }
2334   
 
2335  2 toggle @Override
2336    public String toString() {
2337  2 return "Advice.Dispatcher.OffsetMapping.Target.ForField.ReadOnly{" +
2338    "fieldDescription=" + fieldDescription +
2339    "}";
2340    }
2341    }
2342   
2343    /**
2344    * A target mapping for a field that is both readable and writable.
2345    */
 
2346    protected static class ReadWrite extends ForField {
2347   
2348    /**
2349    * Creates a new field mapping for a field that is readable and writable.
2350    *
2351    * @param fieldDescription The field which is mapped by this target mapping.
2352    */
 
2353  75 toggle protected ReadWrite(FieldDescription.InDefinedShape fieldDescription) {
2354  75 super(fieldDescription);
2355    }
2356   
 
2357  0 toggle @Override
2358    protected int onWriteSingle(MethodVisitor methodVisitor) {
2359  0 if (!fieldDescription.isStatic()) {
2360  0 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
2361  0 methodVisitor.visitInsn(Opcodes.DUP_X1);
2362  0 methodVisitor.visitInsn(Opcodes.POP);
2363  0 accessField(methodVisitor, Opcodes.PUTFIELD);
2364  0 return 2;
2365    } else {
2366  0 accessField(methodVisitor, Opcodes.PUTSTATIC);
2367  0 return NO_PADDING;
2368    }
2369    }
2370   
 
2371  0 toggle @Override
2372    protected int onWriteDouble(MethodVisitor methodVisitor) {
2373  0 if (!fieldDescription.isStatic()) {
2374  0 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
2375  0 methodVisitor.visitInsn(Opcodes.DUP_X2);
2376  0 methodVisitor.visitInsn(Opcodes.POP);
2377  0 accessField(methodVisitor, Opcodes.PUTFIELD);
2378  0 return 2;
2379    } else {
2380  0 accessField(methodVisitor, Opcodes.PUTSTATIC);
2381  0 return NO_PADDING;
2382    }
2383    }
2384   
 
2385  0 toggle @Override
2386    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2387  0 if (fieldDescription.isStatic()) {
2388  0 accessField(methodVisitor, Opcodes.GETSTATIC);
2389  0 methodVisitor.visitInsn(Opcodes.ICONST_1);
2390  0 methodVisitor.visitInsn(Opcodes.IADD);
2391  0 accessField(methodVisitor, Opcodes.PUTSTATIC);
2392  0 return NO_PADDING;
2393    } else {
2394  0 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
2395  0 methodVisitor.visitInsn(Opcodes.DUP);
2396  0 accessField(methodVisitor, Opcodes.GETFIELD);
2397  0 methodVisitor.visitInsn(Opcodes.ICONST_1);
2398  0 methodVisitor.visitInsn(Opcodes.IADD);
2399  0 accessField(methodVisitor, Opcodes.PUTFIELD);
2400  0 return 2;
2401    }
2402    }
2403   
 
2404  2 toggle @Override
2405    public String toString() {
2406  2 return "Advice.Dispatcher.OffsetMapping.Target.ForField.ReadWrite{" +
2407    "fieldDescription=" + fieldDescription +
2408    "}";
2409    }
2410    }
2411    }
2412   
2413    /**
2414    * An offset mapping for a constant pool value.
2415    */
 
2416    class ForConstantPoolValue implements Target {
2417   
2418    /**
2419    * The constant pool value.
2420    */
2421    private final Object value;
2422   
2423    /**
2424    * Creates a mapping for a constant pool value.
2425    *
2426    * @param value The constant pool value.
2427    */
 
2428  86 toggle protected ForConstantPoolValue(Object value) {
2429  86 this.value = value;
2430    }
2431   
 
2432  84 toggle @Override
2433    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2434  84 switch (opcode) {
2435  0 case Opcodes.ISTORE:
2436  1 case Opcodes.ASTORE:
2437  0 case Opcodes.FSTORE:
2438  0 case Opcodes.LSTORE:
2439  0 case Opcodes.DSTORE:
2440  1 throw new IllegalStateException("Cannot write to fixed value: " + value);
2441  0 case Opcodes.ILOAD:
2442  0 case Opcodes.FLOAD:
2443  0 case Opcodes.ALOAD:
2444  0 case Opcodes.LLOAD:
2445  0 case Opcodes.DLOAD:
2446  0 methodVisitor.visitLdcInsn(value);
2447  0 return NO_PADDING;
2448  0 default:
2449  0 throw new IllegalArgumentException("Did not expect opcode: " + opcode);
2450    }
2451    }
2452   
 
2453  0 toggle @Override
2454    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2455  0 throw new IllegalStateException("Cannot write to fixed value: " + value);
2456    }
2457   
 
2458  5 toggle @Override
2459    public boolean equals(Object object) {
2460  1 if (this == object) return true;
2461  2 if (object == null || getClass() != object.getClass()) return false;
2462  2 ForConstantPoolValue that = (ForConstantPoolValue) object;
2463  2 return value.equals(that.value);
2464    }
2465   
 
2466  3 toggle @Override
2467    public int hashCode() {
2468  3 return value.hashCode();
2469    }
2470   
 
2471  3 toggle @Override
2472    public String toString() {
2473  3 return "Advice.Dispatcher.OffsetMapping.Target.ForConstantPoolValue{" +
2474    "value=" + value +
2475    '}';
2476    }
2477    }
2478   
2479    /**
2480    * A target for an offset mapping that boxes a primitive parameter value.
2481    */
 
2482    abstract class ForBoxedParameter implements Target {
2483   
2484    /**
2485    * The parameters offset.
2486    */
2487    protected final int offset;
2488   
2489    /**
2490    * A dispatcher for boxing the primitive value.
2491    */
2492    protected final BoxingDispatcher boxingDispatcher;
2493   
2494    /**
2495    * Creates a new offset mapping for boxing a primitive parameter value.
2496    *
2497    * @param offset The parameters offset.
2498    * @param boxingDispatcher A dispatcher for boxing the primitive value.
2499    */
 
2500  49 toggle protected ForBoxedParameter(int offset, BoxingDispatcher boxingDispatcher) {
2501  49 this.offset = offset;
2502  49 this.boxingDispatcher = boxingDispatcher;
2503    }
2504   
 
2505  0 toggle @Override
2506    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2507  0 switch (opcode) {
2508  0 case Opcodes.ALOAD:
2509  0 boxingDispatcher.loadBoxed(methodVisitor, offset);
2510  0 return boxingDispatcher.getStackSize().getSize() - 1;
2511  0 case Opcodes.ASTORE:
2512  0 onStore(methodVisitor);
2513  0 return NO_PADDING;
2514  0 default:
2515  0 throw new IllegalStateException("Unexpected opcode: " + opcode);
2516    }
2517    }
2518   
2519    /**
2520    * Handles writing the boxed value if applicable.
2521    *
2522    * @param methodVisitor The method visitor for which to apply the writing.
2523    */
2524    protected abstract void onStore(MethodVisitor methodVisitor);
2525   
 
2526  0 toggle @Override
2527    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2528  0 throw new IllegalStateException("Cannot increment a boxed parameter");
2529    }
2530   
 
2531  12 toggle @Override
2532    public boolean equals(Object object) {
2533  2 if (this == object) return true;
2534  4 if (object == null || getClass() != object.getClass()) return false;
2535  6 ForBoxedParameter that = (ForBoxedParameter) object;
2536  6 return offset == that.offset && boxingDispatcher == that.boxingDispatcher;
2537    }
2538   
 
2539  8 toggle @Override
2540    public int hashCode() {
2541  8 int result = offset;
2542  8 result = 31 * result + boxingDispatcher.hashCode();
2543  8 return result;
2544    }
2545   
2546    /**
2547    * A target mapping for a boxed parameter that only allows reading the boxed value.
2548    */
 
2549    protected static class ReadOnly extends ForBoxedParameter {
2550   
2551    /**
2552    * Creates a new read-only target offset mapping for a boxed parameter.
2553    *
2554    * @param offset The parameters offset.
2555    * @param boxingDispatcher A dispatcher for boxing the primitive value.
2556    */
 
2557  36 toggle protected ReadOnly(int offset, BoxingDispatcher boxingDispatcher) {
2558  36 super(offset, boxingDispatcher);
2559    }
2560   
2561    /**
2562    * Creates an appropriate target mapping.
2563    *
2564    * @param offset The parameters offset.
2565    * @param type The primitive type that is boxed or unboxed.
2566    * @return An appropriate target mapping.
2567    */
 
2568  0 toggle protected static Target of(int offset, TypeDefinition type) {
2569  0 return new ReadOnly(offset, BoxingDispatcher.of(type));
2570    }
2571   
 
2572  0 toggle @Override
2573    protected void onStore(MethodVisitor methodVisitor) {
2574  0 throw new IllegalStateException("Cannot write to read-only boxed parameter");
2575    }
2576   
 
2577  2 toggle @Override
2578    public String toString() {
2579  2 return "Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.ReadOnly{" +
2580    "offset=" + offset +
2581    ", boxingDispatcher=" + boxingDispatcher +
2582    '}';
2583    }
2584    }
2585   
2586    /**
2587    * A target mapping for a boxed parameter that allows reading and writing the boxed value.
2588    */
 
2589    protected static class ReadWrite extends ForBoxedParameter {
2590   
2591    /**
2592    * Creates a new read-write target offset mapping for a boxed parameter.
2593    *
2594    * @param offset The parameters offset.
2595    * @param boxingDispatcher A dispatcher for boxing the primitive value.
2596    */
 
2597  13 toggle protected ReadWrite(int offset, BoxingDispatcher boxingDispatcher) {
2598  13 super(offset, boxingDispatcher);
2599    }
2600   
2601    /**
2602    * Creates an appropriate target mapping.
2603    *
2604    * @param offset The parameters offset.
2605    * @param type The primitive type that is boxed or unboxed.
2606    * @return An appropriate target mapping.
2607    */
 
2608  0 toggle protected static Target of(int offset, TypeDefinition type) {
2609  0 return new ReadWrite(offset, BoxingDispatcher.of(type));
2610    }
2611   
 
2612  0 toggle @Override
2613    protected void onStore(MethodVisitor methodVisitor) {
2614  0 boxingDispatcher.storeUnboxed(methodVisitor, offset);
2615    }
2616   
 
2617  2 toggle @Override
2618    public String toString() {
2619  2 return "Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.ReadWrite{" +
2620    "offset=" + offset +
2621    ", boxingDispatcher=" + boxingDispatcher +
2622    '}';
2623    }
2624    }
2625   
2626    /**
2627    * A dispatcher for boxing a primitive value.
2628    */
 
2629    protected enum BoxingDispatcher {
2630   
2631    /**
2632    * A boxing dispatcher for the {@code boolean} type.
2633    */
2634    BOOLEAN(Opcodes.ILOAD, Opcodes.ISTORE, Boolean.class, boolean.class, "booleanValue"),
2635   
2636    /**
2637    * A boxing dispatcher for the {@code byte} type.
2638    */
2639    BYTE(Opcodes.ILOAD, Opcodes.ISTORE, Byte.class, byte.class, "byteValue"),
2640   
2641    /**
2642    * A boxing dispatcher for the {@code short} type.
2643    */
2644    SHORT(Opcodes.ILOAD, Opcodes.ISTORE, Short.class, short.class, "shortValue"),
2645   
2646    /**
2647    * A boxing dispatcher for the {@code char} type.
2648    */
2649    CHARACTER(Opcodes.ILOAD, Opcodes.ISTORE, Character.class, char.class, "charValue"),
2650   
2651    /**
2652    * A boxing dispatcher for the {@code int} type.
2653    */
2654    INTEGER(Opcodes.ILOAD, Opcodes.ISTORE, Integer.class, int.class, "intValue"),
2655   
2656    /**
2657    * A boxing dispatcher for the {@code long} type.
2658    */
2659    LONG(Opcodes.LLOAD, Opcodes.LSTORE, Long.class, long.class, "longValue"),
2660   
2661    /**
2662    * A boxing dispatcher for the {@code float} type.
2663    */
2664    FLOAT(Opcodes.FLOAD, Opcodes.FSTORE, Float.class, float.class, "floatValue"),
2665   
2666    /**
2667    * A boxing dispatcher for the {@code double} type.
2668    */
2669    DOUBLE(Opcodes.DLOAD, Opcodes.DSTORE, Double.class, double.class, "doubleValue");
2670   
2671    /**
2672    * The name of the boxing method of a wrapper type.
2673    */
2674    private static final String VALUE_OF = "valueOf";
2675   
2676    /**
2677    * The opcode to use for loading a value of this type.
2678    */
2679    private final int load;
2680   
2681    /**
2682    * The opcode to use for loading a value of this type.
2683    */
2684    private final int store;
2685   
2686    /**
2687    * The name of the method used for unboxing a primitive type.
2688    */
2689    private final String unboxingMethod;
2690   
2691    /**
2692    * The name of the wrapper type.
2693    */
2694    private final String owner;
2695   
2696    /**
2697    * The descriptor of the boxing method.
2698    */
2699    private final String boxingDescriptor;
2700   
2701    /**
2702    * The descriptor of the unboxing method.
2703    */
2704    private final String unboxingDescriptor;
2705   
2706    /**
2707    * The required stack size of the unboxed value.
2708    */
2709    private final StackSize stackSize;
2710   
2711    /**
2712    * Creates a new boxing dispatcher.
2713    *
2714    * @param load The opcode to use for loading a value of this type.
2715    * @param store The opcode to use for storing a value of this type.
2716    * @param wrapperType The represented wrapper type.
2717    * @param primitiveType The represented primitive type.
2718    * @param unboxingMethod The name of the method used for unboxing a primitive type.
2719    */
 
2720  0 toggle BoxingDispatcher(int load, int store, Class<?> wrapperType, Class<?> primitiveType, String unboxingMethod) {
2721  0 this.load = load;
2722  0 this.store = store;
2723  0 this.unboxingMethod = unboxingMethod;
2724  0 owner = Type.getInternalName(wrapperType);
2725  0 boxingDescriptor = Type.getMethodDescriptor(Type.getType(wrapperType), Type.getType(primitiveType));
2726  0 unboxingDescriptor = Type.getMethodDescriptor(Type.getType(primitiveType));
2727  0 stackSize = StackSize.of(primitiveType);
2728    }
2729   
2730    /**
2731    * Resolves a boxing dispatcher for the supplied primitive type.
2732    *
2733    * @param typeDefinition A description of a primitive type.
2734    * @return An appropriate boxing dispatcher.
2735    */
 
2736  0 toggle protected static BoxingDispatcher of(TypeDefinition typeDefinition) {
2737  0 if (typeDefinition.represents(boolean.class)) {
2738  0 return BOOLEAN;
2739  0 } else if (typeDefinition.represents(byte.class)) {
2740  0 return BYTE;
2741  0 } else if (typeDefinition.represents(short.class)) {
2742  0 return SHORT;
2743  0 } else if (typeDefinition.represents(char.class)) {
2744  0 return CHARACTER;
2745  0 } else if (typeDefinition.represents(int.class)) {
2746  0 return INTEGER;
2747  0 } else if (typeDefinition.represents(long.class)) {
2748  0 return LONG;
2749  0 } else if (typeDefinition.represents(float.class)) {
2750  0 return FLOAT;
2751  0 } else if (typeDefinition.represents(double.class)) {
2752  0 return DOUBLE;
2753    } else {
2754  0 throw new IllegalArgumentException("Cannot box: " + typeDefinition);
2755    }
2756    }
2757   
2758    /**
2759    * Loads the value as a boxed version onto the stack.
2760    *
2761    * @param methodVisitor the method visitor for which to load the value.
2762    * @param offset The offset of the primitive value.
2763    */
 
2764  0 toggle protected void loadBoxed(MethodVisitor methodVisitor, int offset) {
2765  0 methodVisitor.visitVarInsn(load, offset);
2766  0 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, owner, VALUE_OF, boxingDescriptor, false);
2767    }
2768   
2769    /**
2770    * Stores the value as a unboxed version onto the stack.
2771    *
2772    * @param methodVisitor the method visitor for which to store the value.
2773    * @param offset The offset of the primitive value.
2774    */
 
2775  0 toggle protected void storeUnboxed(MethodVisitor methodVisitor, int offset) {
2776  0 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, owner);
2777  0 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, unboxingMethod, unboxingDescriptor, false);
2778  0 methodVisitor.visitVarInsn(store, offset);
2779    }
2780   
2781    /**
2782    * Returns the stack size of the primitive value.
2783    *
2784    * @return The stack size of the primitive value.
2785    */
 
2786  0 toggle protected StackSize getStackSize() {
2787  0 return stackSize;
2788    }
2789   
 
2790  12 toggle @Override
2791    public String toString() {
2792  12 return "Advice.Dispatcher.OffsetMapping.Target.ForBoxedParameter.BoxingDispatcher." + name();
2793    }
2794    }
2795    }
2796   
2797    /**
2798    * A target for an offset mapping of an array containing all (boxed) arguments of the instrumented method.
2799    */
 
2800    class ForBoxedArguments implements Target {
2801   
2802    /**
2803    * The parameters of the instrumented method.
2804    */
2805    private final List<ParameterDescription.InDefinedShape> parameters;
2806   
2807    /**
2808    * Creates a mapping for a boxed array containing all arguments of the instrumented method.
2809    *
2810    * @param parameters The parameters of the instrumented method.
2811    */
 
2812  83 toggle protected ForBoxedArguments(List<ParameterDescription.InDefinedShape> parameters) {
2813  83 this.parameters = parameters;
2814    }
2815   
 
2816  0 toggle @Override
2817    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2818  0 switch (opcode) {
2819  0 case Opcodes.ALOAD:
2820  0 loadInteger(methodVisitor, parameters.size());
2821  0 methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, TypeDescription.OBJECT.getInternalName());
2822  0 StackSize stackSize = StackSize.ZERO;
2823  0 for (ParameterDescription parameter : parameters) {
2824  0 methodVisitor.visitInsn(Opcodes.DUP);
2825  0 loadInteger(methodVisitor, parameter.getIndex());
2826  0 if (parameter.getType().isPrimitive()) {
2827  0 ForBoxedParameter.BoxingDispatcher.of(parameter.getType()).loadBoxed(methodVisitor, parameter.getOffset());
2828    } else {
2829  0 methodVisitor.visitVarInsn(Opcodes.ALOAD, parameter.getOffset());
2830    }
2831  0 methodVisitor.visitInsn(Opcodes.AASTORE);
2832  0 stackSize = stackSize.maximum(parameter.getType().getStackSize());
2833    }
2834  0 return stackSize.getSize() + 2;
2835  0 default:
2836  0 throw new IllegalStateException("Unexpected opcode: " + opcode);
2837    }
2838    }
2839   
2840    /**
2841    * Loads an integer onto the operand stack.
2842    *
2843    * @param methodVisitor The method visitor for which the integer is loaded.
2844    * @param value The integer value to load onto the stack.
2845    */
 
2846  0 toggle private static void loadInteger(MethodVisitor methodVisitor, int value) {
2847  0 switch (value) {
2848  0 case 0:
2849  0 methodVisitor.visitInsn(Opcodes.ICONST_0);
2850  0 break;
2851  0 case 1:
2852  0 methodVisitor.visitInsn(Opcodes.ICONST_1);
2853  0 break;
2854  0 case 2:
2855  0 methodVisitor.visitInsn(Opcodes.ICONST_2);
2856  0 break;
2857  0 case 3:
2858  0 methodVisitor.visitInsn(Opcodes.ICONST_3);
2859  0 break;
2860  0 case 4:
2861  0 methodVisitor.visitInsn(Opcodes.ICONST_4);
2862  0 break;
2863  0 case 5:
2864  0 methodVisitor.visitInsn(Opcodes.ICONST_5);
2865  0 break;
2866  0 default:
2867  0 if (value < Byte.MAX_VALUE) {
2868  0 methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
2869  0 } else if (value < Short.MAX_VALUE) {
2870  0 methodVisitor.visitIntInsn(Opcodes.SIPUSH, value);
2871    } else {
2872  0 methodVisitor.visitLdcInsn(value);
2873    }
2874    }
2875    }
2876   
 
2877  0 toggle @Override
2878    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2879  0 throw new IllegalStateException("Cannot increment a boxed argument");
2880    }
2881   
 
2882  5 toggle @Override
2883    public boolean equals(Object object) {
2884  1 if (this == object) return true;
2885  2 if (object == null || getClass() != object.getClass()) return false;
2886  2 ForBoxedArguments that = (ForBoxedArguments) object;
2887  2 return parameters.equals(that.parameters);
2888    }
2889   
 
2890  3 toggle @Override
2891    public int hashCode() {
2892  3 return parameters.hashCode();
2893    }
2894   
 
2895  3 toggle @Override
2896    public String toString() {
2897  3 return "Advice.Dispatcher.OffsetMapping.Target.ForBoxedArguments{" +
2898    "parameters=" + parameters +
2899    '}';
2900    }
2901    }
2902   
2903    /**
2904    * Binds a null constant to the target parameter.
2905    */
 
2906    enum ForNullConstant implements Target {
2907   
2908    /**
2909    * A null constant that can only be put onto the stack.
2910    */
2911    READ_ONLY {
 
2912  0 toggle @Override
2913    protected void onWrite(MethodVisitor methodVisitor) {
2914  0 throw new IllegalStateException("Cannot write to read-only value");
2915    }
2916    },
2917   
2918    /**
2919    * A null constant that also allows virtual writes where the result is simply popped.
2920    */
2921    READ_WRITE {
 
2922  0 toggle @Override
2923    protected void onWrite(MethodVisitor methodVisitor) {
2924  0 methodVisitor.visitInsn(Opcodes.POP);
2925    }
2926    };
2927   
 
2928  0 toggle @Override
2929    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
2930  0 switch (opcode) {
2931  0 case Opcodes.ALOAD:
2932  0 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
2933  0 break;
2934  0 case Opcodes.ASTORE:
2935  0 onWrite(methodVisitor);
2936  0 break;
2937  0 default:
2938  0 throw new IllegalStateException("Unexpected opcode: " + opcode);
2939    }
2940  0 return NO_PADDING;
2941    }
2942   
2943    /**
2944    * Determines the behavior when writing to the target.
2945    *
2946    * @param methodVisitor The method visitor to which to write the result of the mapping.
2947    */
2948    protected abstract void onWrite(MethodVisitor methodVisitor);
2949   
 
2950  0 toggle @Override
2951    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
2952  0 throw new IllegalStateException("Cannot increment a null constant");
2953    }
2954   
 
2955  2 toggle @Override
2956    public String toString() {
2957  2 return "Advice.Dispatcher.OffsetMapping.Target.ForNullConstant." + name();
2958    }
2959    }
2960   
2961    /**
2962    * Creates a target that represents a value in form of a serialized field.
2963    */
 
2964    class ForSerializedObject implements Target {
2965   
2966    /**
2967    * A charset that does not change the supplied byte array upon encoding or decoding.
2968    */
2969    private static final String CHARSET = "ISO-8859-1";
2970   
2971    /**
2972    * The target type.
2973    */
2974    private final TypeDescription target;
2975   
2976    /**
2977    * The serialized form of the supplied form encoded as a string to be stored in the constant pool.
2978    */
2979    private final String serialized;
2980   
2981    /**
2982    * Creates a target for an offset mapping that references a serialized value.
2983    *
2984    * @param target The target type.
2985    * @param serialized The serialized form of the supplied form encoded as a string to be stored in the constant pool.
2986    */
 
2987  6 toggle protected ForSerializedObject(TypeDescription target, String serialized) {
2988  6 this.target = target;
2989  6 this.serialized = serialized;
2990    }
2991   
2992    /**
2993    * Resolves a serializable value to a target that reads a value from reconstructing a serializable string representation.
2994    *
2995    * @param target The target type of the serializable value.
2996    * @param value The value that the mapped field should represent.
2997    * @return A target for deserializing the supplied value on access.
2998    */
 
2999  0 toggle protected static Target of(TypeDescription target, Serializable value) {
3000  0 try {
3001  0 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
3002  0 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
3003  0 try {
3004  0 objectOutputStream.writeObject(value);
3005    } finally {
3006  0 objectOutputStream.close();
3007    }
3008  0 return new ForSerializedObject(target, byteArrayOutputStream.toString(CHARSET));
3009    } catch (IOException exception) {
3010  0 throw new IllegalStateException("Cannot serialize " + value, exception);
3011    }
3012    }
3013   
 
3014  0 toggle @Override
3015    public int resolveAccess(MethodVisitor methodVisitor, int opcode) {
3016  0 switch (opcode) {
3017  0 case Opcodes.ALOAD:
3018  0 methodVisitor.visitTypeInsn(Opcodes.NEW, Type.getInternalName(ObjectInputStream.class));
3019  0 methodVisitor.visitInsn(Opcodes.DUP);
3020  0 methodVisitor.visitTypeInsn(Opcodes.NEW, Type.getInternalName(ByteArrayInputStream.class));
3021  0 methodVisitor.visitInsn(Opcodes.DUP);
3022  0 methodVisitor.visitLdcInsn(serialized);
3023  0 methodVisitor.visitLdcInsn(CHARSET);
3024  0 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3025    Type.getInternalName(String.class),
3026    "getBytes",
3027    Type.getMethodType(Type.getType(byte[].class), Type.getType(String.class)).toString(),
3028    false);
3029  0 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,
3030    Type.getInternalName(ByteArrayInputStream.class),
3031    MethodDescription.CONSTRUCTOR_INTERNAL_NAME,
3032    Type.getMethodType(Type.VOID_TYPE, Type.getType(byte[].class)).toString(),
3033    false);
3034  0 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,
3035    Type.getInternalName(ObjectInputStream.class),
3036    MethodDescription.CONSTRUCTOR_INTERNAL_NAME,
3037    Type.getMethodType(Type.VOID_TYPE, Type.getType(InputStream.class)).toString(),
3038    false);
3039  0 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
3040    Type.getInternalName(ObjectInputStream.class),
3041    "readObject",
3042    Type.getMethodType(Type.getType(Object.class)).toString(),
3043    false);
3044  0 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, target.getInternalName());
3045  0 return 5;
3046  0 default:
3047  0 throw new IllegalStateException("Unexpected opcode: " + opcode);
3048    }
3049    }
3050   
 
3051  0 toggle @Override
3052    public int resolveIncrement(MethodVisitor methodVisitor, int increment) {
3053  0 throw new IllegalStateException("Cannot increment serialized object");
3054    }
3055   
 
3056  6 toggle @Override
3057    public boolean equals(Object object) {
3058  1 if (this == object) return true;
3059  2 if (object == null || getClass() != object.getClass()) return false;
3060  3 ForSerializedObject that = (ForSerializedObject) object;
3061  3 return target.equals(that.target) && serialized.equals(that.serialized);
3062    }
3063   
 
3064  4 toggle @Override
3065    public int hashCode() {
3066  4 int result = target.hashCode();
3067  4 result = 31 * result + serialized.hashCode();
3068  4 return result;
3069    }
3070   
 
3071  4 toggle @Override
3072    public String toString() {
3073  4 return "Advice.Dispatcher.OffsetMapping.Target.ForSerializedObject{" +
3074    "target=" + target +
3075    ", serialized='" + serialized + '\'' +
3076    '}';
3077    }
3078    }
3079    }
3080   
3081    /**
3082    * Represents a factory for creating a {@link OffsetMapping} for a given parameter.
3083    */
 
3084    interface Factory {
3085   
3086    /**
3087    * Indicates that an offset mapping is undefined.
3088    */
3089    OffsetMapping UNDEFINED = null;
3090   
3091    /**
3092    * Creates a new offset mapping for the supplied parameter if possible.
3093    *
3094    * @param parameterDescription The parameter description for which to resolve an offset mapping.
3095    * @return A resolved offset mapping or {@code null} if no mapping can be resolved for this parameter.
3096    */
3097    OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription);
3098    }
3099   
3100    /**
3101    * An offset mapping for a given parameter of the instrumented method.
3102    */
 
3103    class ForParameter implements OffsetMapping {
3104   
3105    /**
3106    * The index of the parameter.
3107    */
3108    private final int index;
3109   
3110    /**
3111    * Determines if the parameter is to be treated as read-only.
3112    */
3113    private final boolean readOnly;
3114   
3115    /**
3116    * The type expected by the advice method.
3117    */
3118    private final TypeDescription targetType;
3119   
3120    /**
3121    * Creates a new offset mapping for a parameter.
3122    *
3123    * @param argument The annotation for which the mapping is to be created.
3124    * @param targetType Determines if the parameter is to be treated as read-only.
3125    */
 
3126  219 toggle protected ForParameter(Argument argument, TypeDescription targetType) {
3127  219 this(argument.value(), argument.readOnly(), targetType);
3128    }
3129   
3130    /**
3131    * Creates a new offset mapping for a parameter of the instrumented method.
3132    *
3133    * @param index The index of the parameter.
3134    * @param readOnly Determines if the parameter is to be treated as read-only.
3135    * @param targetType The type expected by the advice method.
3136    */
 
3137  230 toggle protected ForParameter(int index, boolean readOnly, TypeDescription targetType) {
3138  230 this.index = index;
3139  230 this.readOnly = readOnly;
3140  230 this.targetType = targetType;
3141    }
3142   
 
3143  219 toggle @Override
3144    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3145  219 ParameterList<?> parameters = instrumentedMethod.getParameters();
3146  219 if (parameters.size() <= index) {
3147  1 throw new IllegalStateException(instrumentedMethod + " does not define an index " + index);
3148  218 } else if (!readOnly && !parameters.get(index).getType().asErasure().equals(targetType)) {
3149  1 throw new IllegalStateException("read-only " + targetType + " is not equal to type of " + parameters.get(index));
3150  217 } else if (readOnly && !parameters.get(index).getType().asErasure().isAssignableTo(targetType)) {
3151  2 throw new IllegalStateException(targetType + " is not assignable to " + parameters.get(index));
3152    }
3153  176 return readOnly
3154    ? new Target.ForParameter.ReadOnly(parameters.get(index).getOffset())
3155    : new Target.ForParameter.ReadWrite(parameters.get(index).getOffset());
3156    }
3157   
 
3158  13 toggle @Override
3159    public boolean equals(Object object) {
3160  2 if (this == object) return true;
3161  4 if (object == null || getClass() != object.getClass()) return false;
3162  7 ForParameter that = (ForParameter) object;
3163  7 return index == that.index
3164    && readOnly == that.readOnly
3165    && targetType.equals(that.targetType);
3166    }
3167   
 
3168  9 toggle @Override
3169    public int hashCode() {
3170  9 int result = index;
3171  9 result = 31 * result + (readOnly ? 1 : 0);
3172  9 result = 31 * result + targetType.hashCode();
3173  9 return result;
3174    }
3175   
 
3176  12 toggle @Override
3177    public String toString() {
3178  12 return "Advice.Dispatcher.OffsetMapping.ForParameter{" +
3179    "index=" + index +
3180    ", readOnly=" + readOnly +
3181    ", targetType=" + targetType +
3182    '}';
3183    }
3184   
3185    /**
3186    * A factory for creating a {@link ForParameter} offset mapping.
3187    */
 
3188    protected enum Factory implements OffsetMapping.Factory {
3189   
3190    /**
3191    * A factory that does not allow writing to the mapped parameter.
3192    */
3193    READ_ONLY(true),
3194   
3195    /**
3196    * A factory that allows writing to the mapped parameter.
3197    */
3198    READ_WRITE(false);
3199   
3200    /**
3201    * {@code true} if the parameter is read-only.
3202    */
3203    private final boolean readOnly;
3204   
3205    /**
3206    * Creates a new factory.
3207    *
3208    * @param readOnly {@code true} if the parameter is read-only.
3209    */
 
3210  0 toggle Factory(boolean readOnly) {
3211  0 this.readOnly = readOnly;
3212    }
3213   
 
3214  1001 toggle @Override
3215    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
3216  1001 AnnotationDescription.Loadable<Argument> annotation = parameterDescription.getDeclaredAnnotations().ofType(Argument.class);
3217  1001 if (annotation == null) {
3218  784 return UNDEFINED;
3219  217 } else if (readOnly && !annotation.loadSilent().readOnly()) {
3220  2 throw new IllegalStateException("Cannot define writable field access for " + parameterDescription);
3221    } else {
3222  215 return new ForParameter(annotation.loadSilent(), parameterDescription.getType().asErasure());
3223    }
3224    }
3225   
 
3226  2 toggle @Override
3227    public String toString() {
3228  2 return "Advice.Dispatcher.OffsetMapping.ForParameter.Factory." + name();
3229    }
3230    }
3231    }
3232   
3233    /**
3234    * An offset mapping that provides access to the {@code this} reference of the instrumented method.
3235    */
 
3236    class ForThisReference implements OffsetMapping {
3237   
3238    /**
3239    * The offset of the this reference in a Java method.
3240    */
3241    private static final int THIS_REFERENCE = 0;
3242   
3243    /**
3244    * Determines if the parameter is to be treated as read-only.
3245    */
3246    private final boolean readOnly;
3247   
3248    /**
3249    * {@code true} if the parameter should be bound to {@code null} if the instrumented method is static.
3250    */
3251    private final boolean optional;
3252   
3253    /**
3254    * The type that the advice method expects for the {@code this} reference.
3255    */
3256    private final TypeDescription targetType;
3257   
3258    /**
3259    * Creates a new offset mapping for a {@code this} reference.
3260    *
3261    * @param readOnly Determines if the parameter is to be treated as read-only.
3262    * @param optional {@code true} if the parameter should be bound to {@code null} if the instrumented method is static.
3263    * @param targetType The type that the advice method expects for the {@code this} reference.
3264    */
 
3265  22 toggle protected ForThisReference(boolean readOnly, boolean optional, TypeDescription targetType) {
3266  22 this.readOnly = readOnly;
3267  22 this.optional = optional;
3268  22 this.targetType = targetType;
3269    }
3270   
 
3271  13 toggle @Override
3272    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3273  13 if (!readOnly && !instrumentedMethod.getDeclaringType().equals(targetType)) {
3274  1 throw new IllegalStateException("Declaring type of " + instrumentedMethod + " is not equal to read-only " + targetType);
3275  12 } else if (readOnly && !instrumentedMethod.getDeclaringType().isAssignableTo(targetType)) {
3276  1 throw new IllegalStateException("Declaring type of " + instrumentedMethod + " is not assignable to " + targetType);
3277  9 } else if (instrumentedMethod.isStatic() && optional) {
3278  0 return readOnly
3279    ? Target.ForNullConstant.READ_ONLY
3280    : Target.ForNullConstant.READ_WRITE;
3281  9 } else if (instrumentedMethod.isStatic() && !optional) {
3282  1 throw new IllegalStateException("Cannot map this reference for static method " + instrumentedMethod);
3283  8 } else if (!context.isInitialized()) {
3284  1 throw new IllegalStateException("Cannot access this reference before calling constructor: " + instrumentedMethod);
3285    }
3286  5 return readOnly
3287    ? new Target.ForParameter.ReadOnly(THIS_REFERENCE)
3288    : new Target.ForParameter.ReadWrite(THIS_REFERENCE);
3289    }
3290   
 
3291  7 toggle @Override
3292    public boolean equals(Object object) {
3293  1 if (this == object) return true;
3294  2 if (object == null || getClass() != object.getClass()) return false;
3295  4 ForThisReference that = (ForThisReference) object;
3296  4 return readOnly == that.readOnly
3297    && optional == that.optional
3298    && targetType.equals(that.targetType);
3299    }
3300   
 
3301  5 toggle @Override
3302    public int hashCode() {
3303  5 int result = (readOnly ? 1 : 0);
3304  5 result = 31 * result + (readOnly ? 1 : 0);
3305  5 result = 31 * result + targetType.hashCode();
3306  5 return result;
3307    }
3308   
 
3309  7 toggle @Override
3310    public String toString() {
3311  7 return "Advice.Dispatcher.OffsetMapping.ForThisReference{" +
3312    "readOnly=" + readOnly +
3313    ", optional=" + optional +
3314    ", targetType=" + targetType +
3315    '}';
3316    }
3317   
3318    /**
3319    * A factory for creating a {@link ForThisReference} offset mapping.
3320    */
 
3321    protected enum Factory implements OffsetMapping.Factory {
3322   
3323    /**
3324    * A factory that does not allow writing to the mapped parameter.
3325    */
3326    READ_ONLY(true),
3327   
3328    /**
3329    * A factory that allows writing to the mapped parameter.
3330    */
3331    READ_WRITE(false);
3332   
3333    /**
3334    * {@code true} if the parameter is read-only.
3335    */
3336    private final boolean readOnly;
3337   
3338    /**
3339    * Creates a new factory.
3340    *
3341    * @param readOnly {@code true} if the parameter is read-only.
3342    */
 
3343  0 toggle Factory(boolean readOnly) {
3344  0 this.readOnly = readOnly;
3345    }
3346   
 
3347  999 toggle @Override
3348    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
3349  999 AnnotationDescription.Loadable<This> annotation = parameterDescription.getDeclaredAnnotations().ofType(This.class);
3350  999 if (annotation == null) {
3351  981 return UNDEFINED;
3352  18 } else if (readOnly && !annotation.loadSilent().readOnly()) {
3353  1 throw new IllegalStateException("Cannot write to this reference for " + parameterDescription + " in read-only context");
3354    } else {
3355  17 return new ForThisReference(annotation.loadSilent().readOnly(),
3356    annotation.loadSilent().optional(),
3357    parameterDescription.getType().asErasure());
3358    }
3359    }
3360   
 
3361  2 toggle @Override
3362    public String toString() {
3363  2 return "Advice.Dispatcher.OffsetMapping.ForThisReference.Factory." + name();
3364    }
3365    }
3366    }
3367   
3368    /**
3369    * Maps the declaring type of the instrumented method.
3370    */
 
3371    enum ForInstrumentedType implements OffsetMapping {
3372   
3373    /**
3374    * The singleton instance.
3375    */
3376    INSTANCE;
3377   
 
3378  0 toggle @Override
3379    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3380  0 return new Target.ForConstantPoolValue(Type.getType(instrumentedMethod.getDeclaringType().getDescriptor()));
3381    }
3382   
 
3383  1 toggle @Override
3384    public String toString() {
3385  1 return "Advice.Dispatcher.OffsetMapping.ForInstrumentedType." + name();
3386    }
3387    }
3388   
3389    /**
3390    * An offset mapping for a field.
3391    */
 
3392    abstract class ForField implements OffsetMapping {
3393   
3394    /**
3395    * The {@link FieldValue#value()} method.
3396    */
3397    private static final MethodDescription.InDefinedShape VALUE;
3398   
3399    /**
3400    * The {@link FieldValue#declaringType()}} method.
3401    */
3402    private static final MethodDescription.InDefinedShape DECLARING_TYPE;
3403   
3404    /**
3405    * The {@link FieldValue#readOnly()}} method.
3406    */
3407    private static final MethodDescription.InDefinedShape READ_ONLY;
3408   
 
3409  1 toggle static {
3410  1 MethodList<MethodDescription.InDefinedShape> methods = new TypeDescription.ForLoadedType(FieldValue.class).getDeclaredMethods();
3411  1 VALUE = methods.filter(named("value")).getOnly();
3412  1 DECLARING_TYPE = methods.filter(named("declaringType")).getOnly();
3413  1 READ_ONLY = methods.filter(named("readOnly")).getOnly();
3414    }
3415   
3416    /**
3417    * The name of the field.
3418    */
3419    protected final String name;
3420   
3421    /**
3422    * The expected type that the field can be assigned to.
3423    */
3424    protected final TypeDescription targetType;
3425   
3426    /**
3427    * {@code true} if this mapping is read-only.
3428    */
3429    protected final boolean readOnly;
3430   
3431    /**
3432    * Creates an offset mapping for a field.
3433    *
3434    * @param name The name of the field.
3435    * @param targetType The expected type that the field can be assigned to.
3436    * @param readOnly {@code true} if this mapping is read-only.
3437    */
 
3438  237 toggle protected ForField(String name, TypeDescription targetType, boolean readOnly) {
3439  237 this.name = name;
3440  237 this.targetType = targetType;
3441  237 this.readOnly = readOnly;
3442    }
3443   
 
3444  225 toggle @Override
3445    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3446  225 FieldLocator.Resolution resolution = fieldLocator(instrumentedMethod.getDeclaringType()).locate(name);
3447  224 if (!resolution.isResolved()) {
3448  1 throw new IllegalStateException("Cannot locate field named " + name + " for " + instrumentedMethod);
3449  223 } else if (readOnly && !resolution.getField().asDefined().getType().asErasure().isAssignableTo(targetType)) {
3450  1 throw new IllegalStateException("Cannot assign type of read-only field " + resolution.getField() + " to " + targetType);
3451  222 } else if (!readOnly && !resolution.getField().asDefined().getType().asErasure().equals(targetType)) {
3452  0 throw new IllegalStateException("Type of field " + resolution.getField() + " is not equal to " + targetType);
3453  222 } else if (!resolution.getField().isStatic() && instrumentedMethod.isStatic()) {
3454  0 throw new IllegalStateException("Cannot read non-static field " + resolution.getField() + " from static method " + instrumentedMethod);
3455  222 } else if (!context.isInitialized() && !resolution.getField().isStatic()) {
3456  1 throw new IllegalStateException("Cannot access non-static field before calling constructor: " + instrumentedMethod);
3457    }
3458  149 return readOnly
3459    ? new Target.ForField.ReadOnly(resolution.getField().asDefined())
3460    : new Target.ForField.ReadWrite(resolution.getField().asDefined());
3461    }
3462   
 
3463  12 toggle @Override
3464    public boolean equals(Object object) {
3465  1 if (this == object) return true;
3466  2 if (object == null || getClass() != object.getClass()) return false;
3467  9 ForField forField = (ForField) object;
3468  9 return name.equals(forField.name) && targetType.equals(forField.targetType) && readOnly == forField.readOnly;
3469    }
3470   
 
3471  11 toggle @Override
3472    public int hashCode() {
3473  11 int result = name.hashCode();
3474  11 result = 31 * result + targetType.hashCode();
3475  11 result = 31 * result + (readOnly ? 1 : 0);
3476  11 return result;
3477    }
3478   
3479    /**
3480    * Returns a field locator for this instance.
3481    *
3482    * @param instrumentedType The instrumented type.
3483    * @return An appropriate field locator.
3484    */
3485    protected abstract FieldLocator fieldLocator(TypeDescription instrumentedType);
3486   
3487    /**
3488    * An offset mapping for a field with an implicit declaring type.
3489    */
 
3490    protected static class WithImplicitType extends ForField {
3491   
3492    /**
3493    * Creates an offset mapping for a field with an implicit declaring type.
3494    *
3495    * @param name The name of the field.
3496    * @param targetType The expected type that the field can be assigned to.
3497    * @param readOnly {@code true} if the field is read-only.
3498    */
 
3499  226 toggle protected WithImplicitType(String name, TypeDescription targetType, boolean readOnly) {
3500  226 super(name, targetType, readOnly);
3501    }
3502   
 
3503  221 toggle @Override
3504    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
3505  221 return new FieldLocator.ForClassHierarchy(instrumentedType);
3506    }
3507   
 
3508  2 toggle @Override
3509    public String toString() {
3510  2 return "Advice.Dispatcher.OffsetMapping.ForField.WithImplicitType{" +
3511    "name=" + name +
3512    ", targetType=" + targetType +
3513    '}';
3514    }
3515    }
3516   
3517    /**
3518    * An offset mapping for a field with an explicit declaring type.
3519    */
 
3520    protected static class WithExplicitType extends ForField {
3521   
3522    /**
3523    * The type declaring the field.
3524    */
3525    private final TypeDescription explicitType;
3526   
3527    /**
3528    * Creates an offset mapping for a field with an explicit declaring type.
3529    *
3530    * @param name The name of the field.
3531    * @param targetType The expected type that the field can be assigned to.
3532    * @param locatedType The type declaring the field.
3533    * @param readOnly {@code true} if the field is read-only.
3534    */
 
3535  11 toggle protected WithExplicitType(String name, TypeDescription targetType, TypeDescription locatedType, boolean readOnly) {
3536  11 super(name, targetType, readOnly);
3537  11 this.explicitType = locatedType;
3538    }
3539   
 
3540  4 toggle @Override
3541    protected FieldLocator fieldLocator(TypeDescription instrumentedType) {
3542  4 if (!instrumentedType.isAssignableTo(explicitType)) {
3543  1 throw new IllegalStateException(explicitType + " is no super type of " + instrumentedType);
3544    }
3545  3 return new FieldLocator.ForExactType(explicitType);
3546    }
3547   
 
3548  8 toggle @Override
3549    public boolean equals(Object object) {
3550  1 if (this == object) return true;
3551  2 if (object == null || getClass() != object.getClass()) return false;
3552  3 if (!super.equals(object)) return false;
3553  2 WithExplicitType that = (WithExplicitType) object;
3554  2 return explicitType.equals(that.explicitType);
3555    }
3556   
 
3557  6 toggle @Override
3558    public int hashCode() {
3559  6 int result = super.hashCode();
3560  6 result = 31 * result + explicitType.hashCode();
3561  6 return result;
3562    }
3563   
 
3564  4 toggle @Override
3565    public String toString() {
3566  4 return "Advice.Dispatcher.OffsetMapping.ForField.WithExplicitType{" +
3567    "name=" + name +
3568    ", targetType=" + targetType +
3569    ", explicitType=" + explicitType +
3570    '}';
3571    }
3572    }
3573   
3574    /**
3575    * A factory for a {@link ForField} offset mapping.
3576    */
 
3577    protected enum Factory implements OffsetMapping.Factory {
3578   
3579    /**
3580    * A factory that does not allow writing to the mapped parameter.
3581    */
3582    READ_ONLY(true),
3583   
3584    /**
3585    * A factory that allows writing to the mapped parameter.
3586    */
3587    READ_WRITE(false);
3588   
3589    /**
3590    * {@code true} if the parameter is read-only.
3591    */
3592    private final boolean readOnly;
3593   
3594    /**
3595    * Creates a new factory.
3596    *
3597    * @param readOnly {@code true} if the parameter is read-only.
3598    */
 
3599  0 toggle Factory(boolean readOnly) {
3600  0 this.readOnly = readOnly;
3601    }
3602   
 
3603  996 toggle @Override
3604    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
3605  996 AnnotationDescription annotation = parameterDescription.getDeclaredAnnotations().ofType(FieldValue.class);
3606  996 if (annotation == null) {
3607  770 return UNDEFINED;
3608  226 } else if (readOnly && !annotation.getValue(ForField.READ_ONLY, Boolean.class)) {
3609  0 throw new IllegalStateException("Cannot write to field for " + parameterDescription + " in read-only context");
3610    } else {
3611  226 TypeDescription declaringType = annotation.getValue(DECLARING_TYPE, TypeDescription.class);
3612  226 String name = annotation.getValue(VALUE, String.class);
3613  226 TypeDescription targetType = parameterDescription.getType().asErasure();
3614  226 return declaringType.represents(void.class)
3615    ? new WithImplicitType(name, targetType, annotation.getValue(ForField.READ_ONLY, Boolean.class))
3616    : new WithExplicitType(name, targetType, declaringType, annotation.getValue(ForField.READ_ONLY, Boolean.class));
3617    }
3618    }
3619   
 
3620  2 toggle @Override
3621    public String toString() {
3622  2 return "Advice.Dispatcher.OffsetMapping.ForField.Factory." + name();
3623    }
3624    }
3625    }
3626   
3627    /**
3628    * An offset mapping for the {@link Advice.Origin} annotation.
3629    */
 
3630    class ForOrigin implements OffsetMapping {
3631   
3632    /**
3633    * The delimiter character.
3634    */
3635    private static final char DELIMITER = '#';
3636   
3637    /**
3638    * The escape character.
3639    */
3640    private static final char ESCAPE = '\\';
3641   
3642    /**
3643    * The renderers to apply.
3644    */
3645    private final List<Renderer> renderers;
3646   
3647    /**
3648    * Creates a new offset mapping for an origin value.
3649    *
3650    * @param renderers The renderers to apply.
3651    */
 
3652  8 toggle protected ForOrigin(List<Renderer> renderers) {
3653  8 this.renderers = renderers;
3654    }
3655   
3656    /**
3657    * Parses a pattern of an origin annotation.
3658    *
3659    * @param pattern The supplied pattern.
3660    * @return An appropriate offset mapping.
3661    */
 
3662  7 toggle protected static OffsetMapping parse(String pattern) {
3663  7 if (pattern.equals(Origin.DEFAULT)) {
3664  3 return new ForOrigin(Collections.<Renderer>singletonList(Renderer.ForStringRepresentation.INSTANCE));
3665    } else {
3666  4 List<Renderer> renderers = new ArrayList<Renderer>(pattern.length());
3667  4 int from = 0;
3668  10 for (int to = pattern.indexOf(DELIMITER); to != -1; to = pattern.indexOf(DELIMITER, from)) {
3669  8 if (to != 0 && pattern.charAt(to - 1) == ESCAPE && (to == 1 || pattern.charAt(to - 2) != ESCAPE)) {
3670  0 renderers.add(new Renderer.ForConstantValue(pattern.substring(from, Math.max(0, to - 1)) + DELIMITER));
3671  0 from = to + 1;
3672  0 continue;
3673  8 } else if (pattern.length() == to + 1) {
3674  1 throw new IllegalStateException("Missing sort descriptor for " + pattern + " at index " + to);
3675    }
3676  7 renderers.add(new Renderer.ForConstantValue(pattern.substring(from, to).replace("" + ESCAPE + ESCAPE, "" + ESCAPE)));
3677  7 switch (pattern.charAt(to + 1)) {
3678  0 case Renderer.ForMethodName.SYMBOL:
3679  0 renderers.add(Renderer.ForMethodName.INSTANCE);
3680  0 break;
3681  0 case Renderer.ForTypeName.SYMBOL:
3682  0 renderers.add(Renderer.ForTypeName.INSTANCE);
3683  0 break;
3684  0 case Renderer.ForDescriptor.SYMBOL:
3685  0 renderers.add(Renderer.ForDescriptor.INSTANCE);
3686  0 break;
3687  0 case Renderer.ForReturnTypeName.SYMBOL:
3688  0 renderers.add(Renderer.ForReturnTypeName.INSTANCE);
3689  0 break;
3690  0 case Renderer.ForJavaSignature.SYMBOL:
3691  0 renderers.add(Renderer.ForJavaSignature.INSTANCE);
3692  0 break;
3693  1 default:
3694  1 throw new IllegalStateException("Illegal sort descriptor " + pattern.charAt(to + 1) + " for " + pattern);
3695    }
3696  0 from = to + 2;
3697    }
3698  0 renderers.add(new Renderer.ForConstantValue(pattern.substring(from)));
3699  0 return new ForOrigin(renderers);
3700    }
3701    }
3702   
 
3703  5 toggle @Override
3704    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3705  5 StringBuilder stringBuilder = new StringBuilder();
3706  5 for (Renderer renderer : renderers) {
3707  19 stringBuilder.append(renderer.apply(instrumentedMethod));
3708    }
3709  5 return new Target.ForConstantPoolValue(stringBuilder.toString());
3710    }
3711   
 
3712  5 toggle @Override
3713    public boolean equals(Object object) {
3714  1 if (this == object) return true;
3715  2 if (object == null || getClass() != object.getClass()) return false;
3716  2 ForOrigin forOrigin = (ForOrigin) object;
3717  2 return renderers.equals(forOrigin.renderers);
3718    }
3719   
 
3720  3 toggle @Override
3721    public int hashCode() {
3722  3 return renderers.hashCode();
3723    }
3724   
 
3725  3 toggle @Override
3726    public String toString() {
3727  3 return "Advice.Dispatcher.OffsetMapping.ForOrigin{" +
3728    "renderers=" + renderers +
3729    '}';
3730    }
3731   
3732    /**
3733    * A renderer for an origin pattern element.
3734    */
 
3735    protected interface Renderer {
3736   
3737    /**
3738    * Returns a string representation for this renderer.
3739    *
3740    * @param instrumentedMethod The method being rendered.
3741    * @return The string representation.
3742    */
3743    String apply(MethodDescription.InDefinedShape instrumentedMethod);
3744   
3745    /**
3746    * A renderer for a method's internal name.
3747    */
 
3748    enum ForMethodName implements Renderer {
3749   
3750    /**
3751    * The singleton instance.
3752    */
3753    INSTANCE;
3754   
3755    /**
3756    * The method name symbol.
3757    */
3758    public static final char SYMBOL = 'm';
3759   
 
3760  0 toggle @Override
3761    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3762  0 return instrumentedMethod.getInternalName();
3763    }
3764   
 
3765  1 toggle @Override
3766    public String toString() {
3767  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForMethodName." + name();
3768    }
3769    }
3770   
3771    /**
3772    * A renderer for a method declaring type's binary name.
3773    */
 
3774    enum ForTypeName implements Renderer {
3775   
3776    /**
3777    * The singleton instance.
3778    */
3779    INSTANCE;
3780   
3781    /**
3782    * The type name symbol.
3783    */
3784    public static final char SYMBOL = 't';
3785   
 
3786  0 toggle @Override
3787    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3788  0 return instrumentedMethod.getDeclaringType().getName();
3789    }
3790   
 
3791  1 toggle @Override
3792    public String toString() {
3793  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForTypeName." + name();
3794    }
3795    }
3796   
3797    /**
3798    * A renderer for a method descriptor.
3799    */
 
3800    enum ForDescriptor implements Renderer {
3801   
3802    /**
3803    * The singleton instance.
3804    */
3805    INSTANCE;
3806   
3807    /**
3808    * The descriptor symbol.
3809    */
3810    public static final char SYMBOL = 'd';
3811   
 
3812  0 toggle @Override
3813    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3814  0 return instrumentedMethod.getDescriptor();
3815    }
3816   
 
3817  1 toggle @Override
3818    public String toString() {
3819  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForDescriptor." + name();
3820    }
3821    }
3822   
3823    /**
3824    * A renderer for a method's Java signature in binary form.
3825    */
 
3826    enum ForJavaSignature implements Renderer {
3827   
3828    /**
3829    * The singleton instance.
3830    */
3831    INSTANCE;
3832   
3833    /**
3834    * The signature symbol.
3835    */
3836    public static final char SYMBOL = 's';
3837   
 
3838  0 toggle @Override
3839    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3840  0 StringBuilder stringBuilder = new StringBuilder("(");
3841  0 boolean comma = false;
3842  0 for (TypeDescription typeDescription : instrumentedMethod.getParameters().asTypeList().asErasures()) {
3843  0 if (comma) {
3844  0 stringBuilder.append(',');
3845    } else {
3846  0 comma = true;
3847    }
3848  0 stringBuilder.append(typeDescription.getName());
3849    }
3850  0 return stringBuilder.append(')').toString();
3851    }
3852   
 
3853  1 toggle @Override
3854    public String toString() {
3855  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForJavaSignature." + name();
3856    }
3857    }
3858   
3859    /**
3860    * A renderer for a method's return type in binary form.
3861    */
 
3862    enum ForReturnTypeName implements Renderer {
3863   
3864    /**
3865    * The singleton instance.
3866    */
3867    INSTANCE;
3868   
3869    /**
3870    * The return type symbol.
3871    */
3872    public static final char SYMBOL = 'r';
3873   
 
3874  0 toggle @Override
3875    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3876  0 return instrumentedMethod.getReturnType().asErasure().getName();
3877    }
3878   
 
3879  1 toggle @Override
3880    public String toString() {
3881  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForReturnTypeName." + name();
3882    }
3883    }
3884   
3885    /**
3886    * A renderer for a method's {@link Object#toString()} representation.
3887    */
 
3888    enum ForStringRepresentation implements Renderer {
3889   
3890    /**
3891    * The singleton instance.
3892    */
3893    INSTANCE;
3894   
 
3895  3 toggle @Override
3896    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3897  3 return instrumentedMethod.toString();
3898    }
3899   
 
3900  1 toggle @Override
3901    public String toString() {
3902  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForStringRepresentation." + name();
3903    }
3904    }
3905   
3906    /**
3907    * A renderer for a constant value.
3908    */
 
3909    class ForConstantValue implements Renderer {
3910   
3911    /**
3912    * The constant value.
3913    */
3914    private final String value;
3915   
3916    /**
3917    * Creates a new renderer for a constant value.
3918    *
3919    * @param value The constant value.
3920    */
 
3921  14 toggle protected ForConstantValue(String value) {
3922  14 this.value = value;
3923    }
3924   
 
3925  0 toggle @Override
3926    public String apply(MethodDescription.InDefinedShape instrumentedMethod) {
3927  0 return value;
3928    }
3929   
 
3930  5 toggle @Override
3931    public boolean equals(Object object) {
3932  1 if (this == object) return true;
3933  2 if (object == null || getClass() != object.getClass()) return false;
3934  2 ForConstantValue that = (ForConstantValue) object;
3935  2 return value.equals(that.value);
3936    }
3937   
 
3938  3 toggle @Override
3939    public int hashCode() {
3940  3 return value.hashCode();
3941    }
3942   
 
3943  3 toggle @Override
3944    public String toString() {
3945  3 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Renderer.ForConstantValue{" +
3946    "value='" + value + '\'' +
3947    '}';
3948    }
3949    }
3950    }
3951   
3952    /**
3953    * A factory for a method origin.
3954    */
 
3955    protected enum Factory implements OffsetMapping.Factory {
3956   
3957    /**
3958    * The singleton instance.
3959    */
3960    INSTANCE;
3961   
 
3962  996 toggle @Override
3963    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
3964  996 AnnotationDescription.Loadable<Origin> origin = parameterDescription.getDeclaredAnnotations().ofType(Origin.class);
3965  996 if (origin == null) {
3966  986 return UNDEFINED;
3967  8 } else if (parameterDescription.getType().asErasure().represents(Class.class)) {
3968  0 return OffsetMapping.ForInstrumentedType.INSTANCE;
3969  8 } else if (parameterDescription.getType().asErasure().isAssignableFrom(String.class)) {
3970  7 return ForOrigin.parse(origin.loadSilent().value());
3971    } else {
3972  1 throw new IllegalStateException("Non-String type " + parameterDescription + " for origin annotation");
3973    }
3974    }
3975   
 
3976  1 toggle @Override
3977    public String toString() {
3978  1 return "Advice.Dispatcher.OffsetMapping.ForOrigin.Factory." + name();
3979    }
3980    }
3981    }
3982   
3983    /**
3984    * An offset mapping for a parameter where assignments are fully ignored and that always return the parameter type's default value.
3985    */
 
3986    enum ForIgnored implements OffsetMapping, Factory {
3987   
3988    /**
3989    * The singleton instance.
3990    */
3991    INSTANCE;
3992   
 
3993  0 toggle @Override
3994    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
3995  0 return Target.ForDefaultValue.INSTANCE;
3996    }
3997   
 
3998  993 toggle @Override
3999    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4000  867 return parameterDescription.getDeclaredAnnotations().isAnnotationPresent(Ignored.class)
4001    ? this
4002    : UNDEFINED;
4003    }
4004   
 
4005  1 toggle @Override
4006    public String toString() {
4007  1 return "Advice.Dispatcher.OffsetMapping.ForIgnored." + name();
4008    }
4009    }
4010   
4011    /**
4012    * An offset mapping that provides access to the value that is returned by the enter advice.
4013    */
 
4014    enum ForEnterValue implements OffsetMapping {
4015   
4016    /**
4017    * Enables writing to the mapped offset.
4018    */
4019    WRITABLE(false),
4020   
4021    /**
4022    * Only allows for reading the mapped offset.
4023    */
4024    READ_ONLY(true);
4025   
4026    /**
4027    * Determines if the parameter is to be treated as read-only.
4028    */
4029    private final boolean readOnly;
4030   
4031    /**
4032    * Creates a new offset mapping for an enter value.
4033    *
4034    * @param readOnly Determines if the parameter is to be treated as read-only.
4035    */
 
4036  0 toggle ForEnterValue(boolean readOnly) {
4037  0 this.readOnly = readOnly;
4038    }
4039   
4040    /**
4041    * Resolves an offset mapping for an enter value.
4042    *
4043    * @param readOnly {@code true} if the value is to be treated as read-only.
4044    * @return An appropriate offset mapping.
4045    */
 
4046  61 toggle public static OffsetMapping of(boolean readOnly) {
4047  60 return readOnly
4048    ? READ_ONLY
4049    : WRITABLE;
4050    }
4051   
 
4052  61 toggle @Override
4053    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4054  60 return readOnly
4055    ? new Target.ForParameter.ReadOnly(instrumentedMethod.getStackSize())
4056    : new Target.ForParameter.ReadWrite(instrumentedMethod.getStackSize());
4057    }
4058   
 
4059  2 toggle @Override
4060    public String toString() {
4061  2 return "Advice.Dispatcher.OffsetMapping.ForEnterValue." + name();
4062    }
4063   
4064    /**
4065    * A factory for creating a {@link ForEnterValue} offset mapping.
4066    */
 
4067    protected static class Factory implements OffsetMapping.Factory {
4068   
4069    /**
4070    * The supplied type of the enter method.
4071    */
4072    private final TypeDescription enterType;
4073   
4074    /**
4075    * Indicates that the mapped parameter is read-only.
4076    */
4077    private final boolean readOnly;
4078   
4079    /**
4080    * Creates a new factory for creating a {@link ForEnterValue} offset mapping.
4081    *
4082    * @param enterType The supplied type of the enter method.
4083    * @param readOnly Indicates that the mapped parameter is read-only.
4084    */
 
4085  268 toggle protected Factory(TypeDescription enterType, boolean readOnly) {
4086  268 this.enterType = enterType;
4087  268 this.readOnly = readOnly;
4088    }
4089   
 
4090  589 toggle @Override
4091    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4092  589 AnnotationDescription.Loadable<Enter> annotation = parameterDescription.getDeclaredAnnotations().ofType(Enter.class);
4093  589 if (annotation != null) {
4094  62 boolean readOnly = annotation.loadSilent().readOnly();
4095  62 if (!readOnly && !enterType.equals(parameterDescription.getType().asErasure())) {
4096  0 throw new IllegalStateException("read-only type of " + parameterDescription + " does not equal " + enterType);
4097  62 } else if (readOnly && !enterType.isAssignableTo(parameterDescription.getType().asErasure())) {
4098  1 throw new IllegalStateException("Cannot assign the type of " + parameterDescription + " to supplied type " + enterType);
4099  61 } else if (this.readOnly && !readOnly) {
4100  0 throw new IllegalStateException("Cannot write to enter value field for " + parameterDescription + " in read only context");
4101    }
4102  61 return ForEnterValue.of(readOnly);
4103    } else {
4104  527 return UNDEFINED;
4105    }
4106    }
4107   
 
4108  6 toggle @Override
4109    public boolean equals(Object object) {
4110  1 if (this == object) return true;
4111  2 if (object == null || getClass() != object.getClass()) return false;
4112  3 Factory factory = (Factory) object;
4113  3 return readOnly == factory.readOnly && enterType.equals(factory.enterType);
4114    }
4115   
 
4116  4 toggle @Override
4117    public int hashCode() {
4118  4 int result = enterType.hashCode();
4119  4 result = 31 * result + (readOnly ? 1 : 0);
4120  4 return result;
4121    }
4122   
 
4123  4 toggle @Override
4124    public String toString() {
4125  4 return "Advice.Dispatcher.OffsetMapping.ForEnterValue.Factory{" +
4126    "enterType=" + enterType +
4127    "m readOnly=" + readOnly +
4128    '}';
4129    }
4130    }
4131    }
4132   
4133    /**
4134    * An offset mapping that provides access to the value that is returned by the instrumented method.
4135    */
 
4136    class ForReturnValue implements OffsetMapping {
4137   
4138    /**
4139    * Determines if the parameter is to be treated as read-only.
4140    */
4141    private final boolean readOnly;
4142   
4143    /**
4144    * The type that the advice method expects for the {@code this} reference.
4145    */
4146    private final TypeDescription targetType;
4147   
4148    /**
4149    * Creates an offset mapping for accessing the return type of the instrumented method.
4150    *
4151    * @param readOnly Determines if the parameter is to be treated as read-only.
4152    * @param targetType The expected target type of the return type.
4153    */
 
4154  67 toggle protected ForReturnValue(boolean readOnly, TypeDescription targetType) {
4155  67 this.readOnly = readOnly;
4156  67 this.targetType = targetType;
4157    }
4158   
 
4159  63 toggle @Override
4160    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4161  63 if (!readOnly && !instrumentedMethod.getReturnType().asErasure().equals(targetType)) {
4162  1 throw new IllegalStateException("read-only return type of " + instrumentedMethod + " is not equal to " + targetType);
4163  62 } else if (readOnly && !instrumentedMethod.getReturnType().asErasure().isAssignableTo(targetType)) {
4164  1 throw new IllegalStateException("Cannot assign return type of " + instrumentedMethod + " to " + targetType);
4165    }
4166  40 return readOnly
4167    ? new Target.ForParameter.ReadOnly(instrumentedMethod.getStackSize() + context.getPadding())
4168    : new Target.ForParameter.ReadWrite(instrumentedMethod.getStackSize() + context.getPadding());
4169    }
4170   
 
4171  6 toggle @Override
4172    public boolean equals(Object other) {
4173  1 if (this == other) return true;
4174  2 if (other == null || getClass() != other.getClass()) return false;
4175  3 ForReturnValue that = (ForReturnValue) other;
4176  3 return readOnly == that.readOnly && targetType.equals(that.targetType);
4177    }
4178   
 
4179  4 toggle @Override
4180    public int hashCode() {
4181  4 return (readOnly ? 1 : 0) + 31 * targetType.hashCode();
4182    }
4183   
 
4184  4 toggle @Override
4185    public String toString() {
4186  4 return "Advice.Dispatcher.OffsetMapping.ForReturnValue{" +
4187    "readOnly=" + readOnly +
4188    ", targetType=" + targetType +
4189    '}';
4190    }
4191   
4192    /**
4193    * A factory for creating a {@link ForReturnValue} offset mapping.
4194    */
 
4195    protected enum Factory implements OffsetMapping.Factory {
4196   
4197    /**
4198    * A factory that does not allow writing to the mapped parameter.
4199    */
4200    READ_ONLY(true),
4201   
4202    /**
4203    * A factory that allows writing to the mapped parameter.
4204    */
4205    READ_WRITE(false);
4206   
4207    /**
4208    * {@code true} if the parameter is read-only.
4209    */
4210    private final boolean readOnly;
4211   
4212    /**
4213    * Creates a new factory.
4214    *
4215    * @param readOnly {@code true} if the parameter is read-only.
4216    */
 
4217  0 toggle Factory(boolean readOnly) {
4218  0 this.readOnly = readOnly;
4219    }
4220   
 
4221  588 toggle @Override
4222    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4223  588 AnnotationDescription.Loadable<Return> annotation = parameterDescription.getDeclaredAnnotations().ofType(Return.class);
4224  588 if (annotation == null) {
4225  525 return UNDEFINED;
4226  63 } else if (readOnly && !annotation.loadSilent().readOnly()) {
4227  0 throw new IllegalStateException("Cannot write return value for " + parameterDescription + " in read-only context");
4228    } else {
4229  63 return new ForReturnValue(annotation.loadSilent().readOnly(), parameterDescription.getType().asErasure());
4230    }
4231    }
4232   
 
4233  2 toggle @Override
4234    public String toString() {
4235  2 return "Advice.Dispatcher.OffsetMapping.ForReturnValue.Factory." + name();
4236    }
4237    }
4238    }
4239   
4240    /**
4241    * An offset mapping for the method's (boxed) return value.
4242    */
 
4243    enum ForBoxedReturnValue implements OffsetMapping {
4244   
4245    /**
4246    * Indicates that it is only legal to read the boxed return value.
4247    */
4248    READ_ONLY(true),
4249   
4250    /**
4251    * A mapping that also allows writing to the boxed return value via a type casting and potential unboxing.
4252    */
4253    READ_WRITE(false);
4254   
4255    /**
4256    * {@code true} if the factory is read-only.
4257    */
4258    private final boolean readOnly;
4259   
4260    /**
4261    * Creates a new offset mapping.
4262    *
4263    * @param readOnly {@code true} if the mapping is read-only.
4264    */
 
4265  0 toggle ForBoxedReturnValue(boolean readOnly) {
4266  0 this.readOnly = readOnly;
4267    }
4268   
 
4269  0 toggle @Override
4270    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4271  0 if (instrumentedMethod.getReturnType().represents(void.class)) {
4272  0 return readOnly
4273    ? Target.ForNullConstant.READ_ONLY
4274    : Target.ForNullConstant.READ_WRITE;
4275  0 } else if (instrumentedMethod.getReturnType().isPrimitive()) {
4276  0 return readOnly
4277    ? Target.ForBoxedParameter.ReadOnly.of(instrumentedMethod.getStackSize() + context.getPadding(), instrumentedMethod.getReturnType())
4278    : Target.ForBoxedParameter.ReadWrite.of(instrumentedMethod.getStackSize() + context.getPadding(), instrumentedMethod.getReturnType());
4279    } else {
4280  0 return readOnly
4281    ? new Target.ForParameter.ReadOnly(instrumentedMethod.getStackSize() + context.getPadding())
4282    : new Target.ForParameter.ReadWrite(instrumentedMethod.getStackSize() + context.getPadding()).casted(instrumentedMethod.getReturnType().asErasure());
4283    }
4284    }
4285   
 
4286  2 toggle @Override
4287    public String toString() {
4288  2 return "Advice.Dispatcher.OffsetMapping.ForBoxedReturnValue." + name();
4289    }
4290   
4291    /**
4292    * A factory for an offset mapping the method's (boxed) return value.
4293    */
 
4294    protected enum Factory implements OffsetMapping.Factory {
4295   
4296    /**
4297    * Indicates that it is only legal to read the boxed return value.
4298    */
4299    READ_ONLY(true),
4300   
4301    /**
4302    * A factory that also allows writing to the boxed return value via a type casting and potential unboxing.
4303    */
4304    READ_WRITE(false);
4305   
4306    /**
4307    * {@code true} if the factory is read-only.
4308    */
4309    private final boolean readOnly;
4310   
4311    /**
4312    * Creates a new factory.
4313    *
4314    * @param readOnly {@code true} if the factory is read-only.
4315    */
 
4316  0 toggle Factory(boolean readOnly) {
4317  0 this.readOnly = readOnly;
4318    }
4319   
 
4320  588 toggle @Override
4321    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4322  588 AnnotationDescription.Loadable<BoxedReturn> annotation = parameterDescription.getDeclaredAnnotations().ofType(BoxedReturn.class);
4323  535 if (annotation == null) {
4324  535 return UNDEFINED;
4325  0 } else if (readOnly && !annotation.loadSilent().readOnly()) {
4326  0 throw new IllegalStateException("Cannot write return value from a non-writable context for " + parameterDescription);
4327  0 } else if (parameterDescription.getType().represents(Object.class)) {
4328  0 return annotation.loadSilent().readOnly()
4329    ? ForBoxedReturnValue.READ_ONLY
4330    : ForBoxedReturnValue.READ_WRITE;
4331    } else {
4332  0 throw new IllegalStateException("Can only assign a boxed return value to an Object type for " + parameterDescription);
4333    }
4334    }
4335   
 
4336  2 toggle @Override
4337    public String toString() {
4338  2 return "Advice.Dispatcher.OffsetMapping.ForBoxedReturnValue.Factory." + name();
4339    }
4340    }
4341    }
4342   
4343    /**
4344    * An offset mapping for an array containing the (boxed) method arguments.
4345    */
 
4346    enum ForBoxedArguments implements OffsetMapping, Factory {
4347   
4348    /**
4349    * The singleton instance.
4350    */
4351    INSTANCE;
4352   
 
4353  0 toggle @Override
4354    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4355  0 return new Target.ForBoxedArguments(instrumentedMethod.getParameters());
4356    }
4357   
 
4358  999 toggle @Override
4359    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4360  919 if (!parameterDescription.getDeclaredAnnotations().isAnnotationPresent(BoxedArguments.class)) {
4361  919 return UNDEFINED;
4362  0 } else if (parameterDescription.getType().represents(Object[].class)) {
4363  0 return this;
4364    } else {
4365  0 throw new IllegalStateException("Can only assign an array of boxed arguments to an Object[] array");
4366    }
4367    }
4368   
 
4369  1 toggle @Override
4370    public String toString() {
4371  1 return "Advice.Dispatcher.OffsetMapping.ForBoxedArguments." + name();
4372    }
4373    }
4374   
4375    /**
4376    * An offset mapping for accessing a {@link Throwable} of the instrumented method.
4377    */
 
4378    class ForThrowable implements OffsetMapping {
4379   
4380    /**
4381    * The type of parameter that is being accessed.
4382    */
4383    private final TypeDescription targetType;
4384   
4385    /**
4386    * The type of the {@link Throwable} being catched if thrown from the instrumented method.
4387    */
4388    private final TypeDescription triggeringThrowable;
4389   
4390    /**
4391    * {@code true} if the parameter is read-only.
4392    */
4393    private final boolean readOnly;
4394   
4395    /**
4396    * Creates a new offset mapping for access of the exception that is thrown by the instrumented method..
4397    *
4398    * @param targetType The type of parameter that is being accessed.
4399    * @param triggeringThrowable The type of the {@link Throwable} being catched if thrown from the instrumented method.
4400    * @param readOnly {@code true} if the parameter is read-only.
4401    */
 
4402  49 toggle protected ForThrowable(TypeDescription targetType, TypeDescription triggeringThrowable, boolean readOnly) {
4403  49 this.targetType = targetType;
4404  49 this.triggeringThrowable = triggeringThrowable;
4405  49 this.readOnly = readOnly;
4406    }
4407   
 
4408  0 toggle @Override
4409    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4410  0 int offset = instrumentedMethod.getStackSize() + context.getPadding() + instrumentedMethod.getReturnType().getStackSize().getSize();
4411  0 return readOnly
4412    ? new Target.ForParameter.ReadOnly(offset)
4413    : new Target.ForParameter.ReadWrite(offset);
4414    }
4415   
 
4416  7 toggle @Override
4417    public boolean equals(Object object) {
4418  1 if (this == object) return true;
4419  2 if (object == null || getClass() != object.getClass()) return false;
4420  4 ForThrowable forThrowable = (ForThrowable) object;
4421  4 return readOnly == forThrowable.readOnly
4422    && targetType.equals(forThrowable.targetType)
4423    && triggeringThrowable.equals(forThrowable.triggeringThrowable);
4424    }
4425   
 
4426  5 toggle @Override
4427    public int hashCode() {
4428  5 int result = triggeringThrowable.hashCode();
4429  5 result = 31 * result + targetType.hashCode();
4430  5 result = 31 * result + (readOnly ? 1 : 0);
4431  5 return result;
4432    }
4433   
 
4434  5 toggle @Override
4435    public String toString() {
4436  5 return "Advice.Dispatcher.OffsetMapping.ForThrowable{" +
4437    "targetType=" + targetType +
4438    ", triggeringThrowable=" + triggeringThrowable +
4439    ", readOnly=" + readOnly +
4440    '}';
4441    }
4442   
4443    /**
4444    * A factory for accessing an exception that was thrown by the instrumented method.
4445    */
 
4446    protected static class Factory implements OffsetMapping.Factory {
4447   
4448    /**
4449    * The type of the {@link Throwable} being catched if thrown from the instrumented method.
4450    */
4451    private final TypeDescription triggeringThrowable;
4452   
4453    /**
4454    * {@code true} if the parameter is read-only.
4455    */
4456    private final boolean readOnly;
4457   
4458    /**
4459    * Creates a new factory for access of the exception that is thrown by the instrumented method..
4460    *
4461    * @param triggeringThrowable The type of the {@link Throwable} being catched if thrown from the instrumented method.
4462    * @param readOnly {@code true} if the parameter is read-only.
4463    */
 
4464  106 toggle protected Factory(TypeDescription triggeringThrowable, boolean readOnly) {
4465  106 this.triggeringThrowable = triggeringThrowable;
4466  106 this.readOnly = readOnly;
4467    }
4468   
4469    /**
4470    * Resolves an appropriate offset mapping factory for the {@link Thrown} parameter annotation.
4471    *
4472    * @param adviceMethod The exit advice method, annotated with {@link OnMethodExit}.
4473    * @param readOnly {@code true} if the parameter is read-only.
4474    * @return An appropriate offset mapping factory.
4475    */
 
4476  264 toggle @SuppressWarnings("all") // In absence of @SafeVarargs for Java 6
4477    protected static OffsetMapping.Factory of(MethodDescription.InDefinedShape adviceMethod, boolean readOnly) {
4478  264 TypeDescription triggeringThrowable = adviceMethod.getDeclaredAnnotations()
4479    .ofType(OnMethodExit.class)
4480    .getValue(ON_THROWABLE, TypeDescription.class);
4481  264 return triggeringThrowable.represents(NoExceptionHandler.class)
4482    ? new OffsetMapping.Illegal(Thrown.class)
4483    : new Factory(triggeringThrowable, readOnly);
4484    }
4485   
 
4486  0 toggle @Override
4487    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4488  0 AnnotationDescription.Loadable<Thrown> annotation = parameterDescription.getDeclaredAnnotations().ofType(Thrown.class);
4489  0 if (annotation == null) {
4490  0 return UNDEFINED;
4491  0 } else if (!parameterDescription.getType().represents(Throwable.class)) {
4492  0 throw new IllegalStateException("Parameter must be a throwable type for " + parameterDescription);
4493  0 } else if (readOnly && !annotation.loadSilent().readOnly()) {
4494  0 throw new IllegalStateException("Cannot write exception value for " + parameterDescription + " in read-only context");
4495    } else {
4496  0 return new ForThrowable(parameterDescription.getType().asErasure(), triggeringThrowable, annotation.loadSilent().readOnly());
4497    }
4498    }
4499   
 
4500  0 toggle @Override
4501    public boolean equals(Object object) {
4502  0 if (this == object) return true;
4503  0 if (object == null || getClass() != object.getClass()) return false;
4504  0 Factory factory = (Factory) object;
4505  0 return readOnly == factory.readOnly && triggeringThrowable.equals(factory.triggeringThrowable);
4506    }
4507   
 
4508  0 toggle @Override
4509    public int hashCode() {
4510  0 int result = triggeringThrowable.hashCode();
4511  0 result = 31 * result + (readOnly ? 1 : 0);
4512  0 return result;
4513    }
4514   
 
4515  0 toggle @Override
4516    public String toString() {
4517  0 return "Advice.Dispatcher.OffsetMapping.ForThrowable.Factory{" +
4518    "triggeringThrowable=" + triggeringThrowable +
4519    ", readOnly=" + readOnly +
4520    '}';
4521    }
4522    }
4523    }
4524   
4525    /**
4526    * Represents an offset mapping for a user-defined value.
4527    *
4528    * @param <T> The mapped annotation type.
4529    */
 
4530    class ForUserValue<T extends Annotation> implements OffsetMapping {
4531   
4532    /**
4533    * The target parameter that is bound.
4534    */
4535    private final ParameterDescription.InDefinedShape target;
4536   
4537    /**
4538    * The annotation value that triggered the binding.
4539    */
4540    private final AnnotationDescription.Loadable<T> annotation;
4541   
4542    /**
4543    * The dynamic value that is bound.
4544    */
4545    private final DynamicValue<T> dynamicValue;
4546   
4547    /**
4548    * Creates a new offset mapping for a user-defined value.
4549    *
4550    * @param target The target parameter that is bound.
4551    * @param annotation The annotation value that triggered the binding.
4552    * @param dynamicValue The dynamic value that is bound.
4553    */
 
4554  97 toggle protected ForUserValue(ParameterDescription.InDefinedShape target,
4555    AnnotationDescription.Loadable<T> annotation,
4556    DynamicValue<T> dynamicValue) {
4557  97 this.target = target;
4558  97 this.annotation = annotation;
4559  97 this.dynamicValue = dynamicValue;
4560    }
4561   
 
4562  89 toggle @Override
4563    public Target resolve(MethodDescription.InDefinedShape instrumentedMethod, Context context) {
4564  89 Object value = dynamicValue.resolve(instrumentedMethod, target, annotation, context.isInitialized());
4565  89 if (value == null) {
4566  1 if (target.getType().isPrimitive()) {
4567  1 throw new IllegalStateException("Cannot map null to primitive type of " + target);
4568    }
4569  0 return Target.ForNullConstant.READ_ONLY;
4570  8 } else if ((target.getType().asErasure().isAssignableFrom(String.class) && value instanceof String)
4571    || (target.getType().isPrimitive() && target.getType().asErasure().isInstanceOrWrapper(value))) {
4572  0 if (value instanceof Boolean) {
4573  0 value = (Boolean) value ? 1 : 0;
4574  0 } else if (value instanceof Byte) {
4575  0 value = ((Byte) value).intValue();
4576  0 } else if (value instanceof Short) {
4577  0 value = ((Short) value).intValue();
4578  0 } else if (value instanceof Character) {
4579  0 value = (int) ((Character) value).charValue();
4580    }
4581  0 return new Target.ForConstantPoolValue(value);
4582  6 } else if (target.getType().asErasure().isAssignableFrom(Class.class) && value instanceof Class) {
4583  0 return new Target.ForConstantPoolValue(Type.getType((Class<?>) value));
4584  4 } else if (target.getType().asErasure().isAssignableFrom(Class.class) && value instanceof TypeDescription) {
4585  0 return new Target.ForConstantPoolValue(Type.getType(((TypeDescription) value).getDescriptor()));
4586  2 } else if (!target.getType().isPrimitive() && !target.getType().isArray() && value instanceof Serializable && target.getType().asErasure().isInstance(value)) {
4587  0 return Target.ForSerializedObject.of(target.getType().asErasure(), (Serializable) value);
4588    } else {
4589  2 throw new IllegalStateException("Cannot map " + value + " as constant value of " + target.getType());
4590    }
4591    }
4592   
 
4593  7 toggle @Override
4594    public boolean equals(Object object) {
4595  1 if (this == object) return true;
4596  2 if (object == null || getClass() != object.getClass()) return false;
4597  4 ForUserValue that = (ForUserValue) object;
4598  4 return target.equals(that.target)
4599    && annotation.equals(that.annotation)
4600    && dynamicValue.equals(that.dynamicValue);
4601    }
4602   
 
4603  5 toggle @Override
4604    public int hashCode() {
4605  5 int result = target.hashCode();
4606  5 result = 31 * result + annotation.hashCode();
4607  5 result = 31 * result + dynamicValue.hashCode();
4608  5 return result;
4609    }
4610   
 
4611  5 toggle @Override
4612    public String toString() {
4613  5 return "Advice.Dispatcher.OffsetMapping.ForUserValue{" +
4614    "target=" + target +
4615    ", annotation=" + annotation +
4616    ", dynamicValue=" + dynamicValue +
4617    '}';
4618    }
4619   
4620    /**
4621    * A factory for mapping a user-defined dynamic value.
4622    *
4623    * @param <S> The mapped annotation type.
4624    */
 
4625    protected static class Factory<S extends Annotation> implements OffsetMapping.Factory {
4626   
4627    /**
4628    * The mapped annotation type.
4629    */
4630    private final Class<S> type;
4631   
4632    /**
4633    * The dynamic value instance used for resolving a binding.
4634    */
4635    private final DynamicValue<S> dynamicValue;
4636   
4637    /**
4638    * Creates a new factory for a user-defined dynamic value.
4639    *
4640    * @param type The mapped annotation type.
4641    * @param dynamicValue The dynamic value instance used for resolving a binding.
4642    */
 
4643  50 toggle protected Factory(Class<S> type, DynamicValue<S> dynamicValue) {
4644  50 this.type = type;
4645  50 this.dynamicValue = dynamicValue;
4646    }
4647   
4648    /**
4649    * Creates a new factory for mapping a user value.
4650    *
4651    * @param type The mapped annotation type.
4652    * @param dynamicValue The dynamic value instance used for resolving a binding.
4653    * @return An appropriate factory for such a offset mapping.
4654    */
 
4655  46 toggle @SuppressWarnings("unchecked")
4656    protected static OffsetMapping.Factory of(Class<? extends Annotation> type, DynamicValue<?> dynamicValue) {
4657  46 return new Factory(type, dynamicValue);
4658    }
4659   
 
4660  684 toggle @Override
4661    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4662  684 AnnotationDescription.Loadable<S> annotation = parameterDescription.getDeclaredAnnotations().ofType(type);
4663  92 return annotation == null
4664    ? UNDEFINED
4665    : new ForUserValue<S>(parameterDescription, annotation, dynamicValue);
4666    }
4667   
 
4668  6 toggle @Override
4669    public boolean equals(Object object) {
4670  1 if (this == object) return true;
4671  2 if (object == null || getClass() != object.getClass()) return false;
4672  3 Factory factory = (Factory) object;
4673  3 return type.equals(factory.type) && dynamicValue.equals(factory.dynamicValue);
4674    }
4675   
 
4676  4 toggle @Override
4677    public int hashCode() {
4678  4 int result = type.hashCode();
4679  4 result = 31 * result + dynamicValue.hashCode();
4680  4 return result;
4681    }
4682   
 
4683  4 toggle @Override
4684    public String toString() {
4685  4 return "Advice.Dispatcher.OffsetMapping.ForUserValue.Factory{" +
4686    "type=" + type +
4687    ", dynamicValue=" + dynamicValue +
4688    '}';
4689    }
4690    }
4691    }
4692   
4693    /**
4694    * Represents a factory that throws an exception for a given set of illegal parameter annotations.
4695    */
 
4696    class Illegal implements Factory {
4697   
4698    /**
4699    * The set of illegal annotations.
4700    */
4701    private final List<? extends Class<? extends Annotation>> annotations;
4702   
4703    /**
4704    * Creates a new factory for restricting the use of illegal annotation types.
4705    *
4706    * @param annotation The set of illegal annotations.
4707    */
4708    //@SafeVarargs
 
4709  370 toggle protected Illegal(Class<? extends Annotation>... annotation) {
4710  370 this(Arrays.asList(annotation));
4711    }
4712   
4713    /**
4714    * Creates a new factory for restricting the use of illegal annotation types.
4715    *
4716    * @param annotations The set of illegal annotations.
4717    */
 
4718  373 toggle protected Illegal(List<? extends Class<? extends Annotation>> annotations) {
4719  373 this.annotations = annotations;
4720    }
4721   
 
4722  514 toggle @Override
4723    public OffsetMapping make(ParameterDescription.InDefinedShape parameterDescription) {
4724  514 for (Class<? extends Annotation> annotation : annotations) {
4725  1723 if (parameterDescription.getDeclaredAnnotations().isAnnotationPresent(annotation)) {
4726  6 throw new IllegalStateException("Illegal annotation " + annotation + " for " + parameterDescription);
4727    }
4728    }
4729  508 return UNDEFINED;
4730    }
4731   
 
4732  10 toggle @Override
4733    public boolean equals(Object other) {
4734  2 if (this == other) return true;
4735  4 if (other == null || getClass() != other.getClass()) return false;
4736  4 Illegal illegal = (Illegal) other;
4737  4 return annotations.equals(illegal.annotations);
4738    }
4739   
 
4740  6 toggle @Override
4741    public int hashCode() {
4742  6 return annotations.hashCode();
4743    }
4744   
 
4745  6 toggle @Override
4746    public String toString() {
4747  6 return "Advice.Dispatcher.OffsetMapping.Illegal{" +
4748    "annotations=" + annotations +
4749    '}';
4750    }
4751    }
4752    }
4753   
4754    /**
4755    * A suppression handler for optionally suppressing exceptions.
4756    */
 
4757    interface SuppressionHandler {
4758   
4759    /**
4760    * Binds the suppression handler for instrumenting a specific method.
4761    *
4762    * @return A bound version of the suppression handler.
4763    */
4764    Bound bind();
4765   
4766    /**
4767    * A producer for a default return value if this is applicable.
4768    */
 
4769    interface ReturnValueProducer {
4770   
4771    /**
4772    * Instructs this return value producer to assure the production of a default value for the return type of the currently handled method.
4773    */
4774    void onDefaultValue();
4775    }
4776   
4777    /**
4778    * A bound version of a suppression handler that must not be reused.
4779    */
 
4780    interface Bound {
4781   
4782    /**
4783    * Invoked to prepare the suppression handler, i.e. to write an exception handler entry if appropriate.
4784    *
4785    * @param methodVisitor The method visitor to apply the preparation to.
4786    */
4787    void onPrepare(MethodVisitor methodVisitor);
4788   
4789    /**
4790    * Invoked at the start of a method.
4791    *
4792    * @param methodVisitor The method visitor of the instrumented method.
4793    * @param methodSizeHandler A handler for computing the method size requirements.
4794    */
4795    void onStart(MethodVisitor methodVisitor, MethodSizeHandler.ForAdvice methodSizeHandler);
4796   
4797    /**
4798    * Invoked at the end of a method.
4799    *
4800    * @param methodVisitor The method visitor of the instrumented method.
4801    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
4802    * @param returnValueProducer A producer for defining a default return value of the advised method.
4803    */
4804    void onEnd(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer);
4805   
4806    /**
4807    * Invoked at the end of a method. Additionally indicates that the handler block should be surrounding by a skipping instruction. This method
4808    * is always followed by a stack map frame (if it is required for the class level and class writer setting).
4809    *
4810    * @param methodVisitor The method visitor of the instrumented method.
4811    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
4812    * @param returnValueProducer A producer for defining a default return value of the advised method.
4813    */
4814    void onEndSkipped(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer);
4815    }
4816   
4817    /**
4818    * A non-operational suppression handler that does not suppress any method.
4819    */
 
4820    enum NoOp implements SuppressionHandler, Bound {
4821   
4822    /**
4823    * The singleton instance.
4824    */
4825    INSTANCE;
4826   
 
4827  339 toggle @Override
4828    public Bound bind() {
4829  339 return this;
4830    }
4831   
 
4832  339 toggle @Override
4833    public void onPrepare(MethodVisitor methodVisitor) {
4834    /* do nothing */
4835    }
4836   
 
4837  316 toggle @Override
4838    public void onStart(MethodVisitor methodVisitor, MethodSizeHandler.ForAdvice methodSizeHandler) {
4839    /* do nothing */
4840    }
4841   
 
4842  250 toggle @Override
4843    public void onEnd(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer) {
4844    /* do nothing */
4845    }
4846   
 
4847  0 toggle @Override
4848    public void onEndSkipped(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer) {
4849    /* do nothing */
4850    }
4851   
 
4852  1 toggle @Override
4853    public String toString() {
4854  1 return "Advice.Dispatcher.SuppressionHandler.NoOp." + name();
4855    }
4856    }
4857   
4858    /**
4859    * A suppression handler that suppresses a given throwable type.
4860    */
 
4861    class Suppressing implements SuppressionHandler {
4862   
4863    /**
4864    * The suppressed throwable type.
4865    */
4866    private final TypeDescription suppressedType;
4867   
4868    /**
4869    * Creates a new suppressing suppression handler.
4870    *
4871    * @param suppressedType The suppressed throwable type.
4872    */
 
4873  134 toggle protected Suppressing(TypeDescription suppressedType) {
4874  134 this.suppressedType = suppressedType;
4875    }
4876   
4877    /**
4878    * Resolves an appropriate suppression handler.
4879    *
4880    * @param suppressedType The suppressed type or {@link NoExceptionHandler} if no type should be suppressed.
4881    * @return An appropriate suppression handler.
4882    */
 
4883  458 toggle protected static SuppressionHandler of(TypeDescription suppressedType) {
4884  327 return suppressedType.represents(NoExceptionHandler.class)
4885    ? NoOp.INSTANCE
4886    : new Suppressing(suppressedType);
4887    }
4888   
 
4889  0 toggle @Override
4890    public SuppressionHandler.Bound bind() {
4891  0 return new Bound(suppressedType);
4892    }
4893   
 
4894  5 toggle @Override
4895    public boolean equals(Object object) {
4896  1 if (this == object) return true;
4897  2 if (object == null || getClass() != object.getClass()) return false;
4898  2 Suppressing that = (Suppressing) object;
4899  2 return suppressedType.equals(that.suppressedType);
4900    }
4901   
 
4902  3 toggle @Override
4903    public int hashCode() {
4904  3 return suppressedType.hashCode();
4905    }
4906   
 
4907  3 toggle @Override
4908    public String toString() {
4909  3 return "Advice.Dispatcher.SuppressionHandler.Suppressing{" +
4910    "suppressedType=" + suppressedType +
4911    '}';
4912    }
4913   
4914    /**
4915    * An active, bound suppression handler.
4916    */
 
4917    protected static class Bound implements SuppressionHandler.Bound {
4918   
4919    /**
4920    * The suppressed throwable type.
4921    */
4922    private final TypeDescription suppressedType;
4923   
4924    /**
4925    * A label indicating the start of the method.
4926    */
4927    private final Label startOfMethod;
4928   
4929    /**
4930    * A label indicating the end of the method.
4931    */
4932    private final Label endOfMethod;
4933   
4934    /**
4935    * Creates a new active, bound suppression handler.
4936    *
4937    * @param suppressedType The suppressed throwable type.
4938    */
 
4939  149 toggle protected Bound(TypeDescription suppressedType) {
4940  149 this.suppressedType = suppressedType;
4941  149 startOfMethod = new Label();
4942  149 endOfMethod = new Label();
4943    }
4944   
 
4945  0 toggle @Override
4946    public void onPrepare(MethodVisitor methodVisitor) {
4947  0 methodVisitor.visitTryCatchBlock(startOfMethod, endOfMethod, endOfMethod, suppressedType.getInternalName());
4948    }
4949   
 
4950  0 toggle @Override
4951    public void onStart(MethodVisitor methodVisitor, MethodSizeHandler.ForAdvice methodSizeHandler) {
4952  0 methodVisitor.visitLabel(startOfMethod);
4953  0 methodSizeHandler.recordMinimum(StackSize.SINGLE.getSize());
4954    }
4955   
 
4956  0 toggle @Override
4957    public void onEnd(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer) {
4958  0 methodVisitor.visitLabel(endOfMethod);
4959  0 stackMapFrameHandler.injectExceptionFrame(methodVisitor);
4960  0 methodVisitor.visitInsn(Opcodes.POP);
4961  0 returnValueProducer.onDefaultValue();
4962    }
4963   
 
4964  0 toggle @Override
4965    public void onEndSkipped(MethodVisitor methodVisitor, StackMapFrameHandler.ForAdvice stackMapFrameHandler, ReturnValueProducer returnValueProducer) {
4966  0 Label endOfHandler = new Label();
4967  0 methodVisitor.visitJumpInsn(Opcodes.GOTO, endOfHandler);
4968  0 onEnd(methodVisitor, stackMapFrameHandler, returnValueProducer);
4969  0 methodVisitor.visitLabel(endOfHandler);
4970    }
4971   
 
4972  5 toggle @Override
4973    public String toString() {
4974  5 return "Advice.Dispatcher.SuppressionHandler.Suppressing.Bound{" +
4975    "suppressedType=" + suppressedType +
4976    ", startOfMethod=" + startOfMethod +
4977    ", endOfMethod=" + endOfMethod +
4978    '}';
4979    }
4980    }
4981    }
4982    }
4983   
4984    /**
4985    * Represents a resolved dispatcher.
4986    */
 
4987    interface Resolved extends Dispatcher {
4988   
4989    /**
4990    * Binds this dispatcher for resolution to a specific method.
4991    *
4992    * @param instrumentedMethod The instrumented method.
4993    * @param methodVisitor The method visitor for writing the instrumented method.
4994    * @param methodSizeHandler A handler for computing the method size requirements.
4995    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
4996    * @return A dispatcher that is bound to the instrumented method.
4997    */
4998    Bound bind(MethodDescription.InDefinedShape instrumentedMethod,
4999    MethodVisitor methodVisitor,
5000    MethodSizeHandler methodSizeHandler,
5001    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler);
5002   
5003    /**
5004    * Represents a resolved dispatcher for entering a method.
5005    */
 
5006    interface ForMethodEnter extends Resolved {
5007   
5008    /**
5009    * Returns the type that this dispatcher supplies as a result of its advice or a description of {@code void} if
5010    * no type is supplied as a result of the enter advice.
5011    *
5012    * @return The type that this dispatcher supplies as a result of its advice or a description of {@code void}.
5013    */
5014    TypeDescription getEnterType();
5015    }
5016   
5017    /**
5018    * Represents a resolved dispatcher for exiting a method.
5019    */
 
5020    interface ForMethodExit extends Resolved {
5021   
5022    /**
5023    * Returns the type of throwable for which this exit advice is supposed to be invoked.
5024    *
5025    * @return The {@link Throwable} type for which to invoke this exit advice or a description of {@link NoExceptionHandler}
5026    * if this exit advice does not expect to be invoked upon any throwable.
5027    */
5028    TypeDescription getTriggeringThrowable();
5029    }
5030    }
5031   
5032    /**
5033    * A bound resolution of an advice method.
5034    */
 
5035    interface Bound {
5036   
5037    /**
5038    * Prepares the advice method's exception handlers.
5039    */
5040    void prepare();
5041   
5042    /**
5043    * Writes the advice method's code.
5044    */
5045    void apply();
5046    }
5047   
5048    /**
5049    * An implementation for inactive devise that does not write any byte code.
5050    */
 
5051    enum Inactive implements Dispatcher.Unresolved, Resolved.ForMethodEnter, Resolved.ForMethodExit, Bound {
5052   
5053    /**
5054    * The singleton instance.
5055    */
5056    INSTANCE;
5057   
 
5058  630 toggle @Override
5059    public boolean isAlive() {
5060  630 return false;
5061    }
5062   
 
5063  107 toggle @Override
5064    public boolean isBinary() {
5065  107 return false;
5066    }
5067   
 
5068  0 toggle @Override
5069    public TypeDescription getTriggeringThrowable() {
5070  0 return NoExceptionHandler.DESCRIPTION;
5071    }
5072   
 
5073  288 toggle @Override
5074    public TypeDescription getEnterType() {
5075  288 return TypeDescription.VOID;
5076    }
5077   
 
5078  104 toggle @Override
5079    public Resolved.ForMethodEnter asMethodEnter(List<? extends OffsetMapping.Factory> userFactories,
5080    ClassFileLocator.Resolution binaryRepresentation) {
5081  104 return this;
5082    }
5083   
 
5084  45 toggle @Override
5085    public Resolved.ForMethodExit asMethodExitTo(List<? extends OffsetMapping.Factory> userFactories,
5086    ClassFileLocator.Resolution binaryRepresentation,
5087    ForMethodEnter dispatcher) {
5088  45 return this;
5089    }
5090   
 
5091  136 toggle @Override
5092    public void prepare() {
5093    /* do nothing */
5094    }
5095   
 
5096  92 toggle @Override
5097    public void apply() {
5098    /* do nothing */
5099    }
5100   
 
5101  136 toggle @Override
5102    public Bound bind(MethodDescription.InDefinedShape instrumentedMethod,
5103    MethodVisitor methodVisitor,
5104    MethodSizeHandler methodSizeHandler,
5105    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler) {
5106  136 return this;
5107    }
5108   
 
5109  1 toggle @Override
5110    public String toString() {
5111  1 return "Advice.Dispatcher.Inactive." + name();
5112    }
5113    }
5114   
5115    /**
5116    * A dispatcher for an advice method that is being inlined into the instrumented method.
5117    */
 
5118    class Inlining implements Unresolved {
5119   
5120    /**
5121    * The advice method.
5122    */
5123    protected final MethodDescription.InDefinedShape adviceMethod;
5124   
5125    /**
5126    * Creates a dispatcher for inlined advice method.
5127    *
5128    * @param adviceMethod The advice method.
5129    */
 
5130  403 toggle protected Inlining(MethodDescription.InDefinedShape adviceMethod) {
5131  403 this.adviceMethod = adviceMethod;
5132    }
5133   
 
5134  268 toggle @Override
5135    public boolean isAlive() {
5136  268 return true;
5137    }
5138   
 
5139  267 toggle @Override
5140    public boolean isBinary() {
5141  267 return true;
5142    }
5143   
 
5144  177 toggle @Override
5145    public Dispatcher.Resolved.ForMethodEnter asMethodEnter(List<? extends OffsetMapping.Factory> userFactories,
5146    ClassFileLocator.Resolution binaryRepresentation) {
5147  177 return new Resolved.ForMethodEnter(adviceMethod, userFactories, binaryRepresentation.resolve());
5148    }
5149   
 
5150  219 toggle @Override
5151    public Dispatcher.Resolved.ForMethodExit asMethodExitTo(List<? extends OffsetMapping.Factory> userFactories,
5152    ClassFileLocator.Resolution binaryRepresentation,
5153    Dispatcher.Resolved.ForMethodEnter dispatcher) {
5154  219 return Resolved.ForMethodExit.of(adviceMethod, userFactories, binaryRepresentation.resolve(), dispatcher.getEnterType());
5155    }
5156   
 
5157  5 toggle @Override
5158    public boolean equals(Object other) {
5159  5 return this == other || !(other == null || getClass() != other.getClass()) && adviceMethod.equals(((Inlining) other).adviceMethod);
5160    }
5161   
 
5162  3 toggle @Override
5163    public int hashCode() {
5164  3 return adviceMethod.hashCode();
5165    }
5166   
 
5167  4 toggle @Override
5168    public String toString() {
5169  4 return "Advice.Dispatcher.Inlining{" +
5170    "adviceMethod=" + adviceMethod +
5171    '}';
5172    }
5173   
5174    /**
5175    * A resolved version of a dispatcher.
5176    */
 
5177    protected abstract static class Resolved implements Dispatcher.Resolved {
5178   
5179    /**
5180    * Indicates a read-only mapping for an offset.
5181    */
5182    private static final boolean READ_ONLY = true;
5183   
5184    /**
5185    * The represented advice method.
5186    */
5187    protected final MethodDescription.InDefinedShape adviceMethod;
5188   
5189    /**
5190    * The binary representation of the advice method.
5191    */
5192    private final byte[] binaryRepresentation;
5193   
5194    /**
5195    * An unresolved mapping of offsets of the advice method based on the annotations discovered on each method parameter.
5196    */
5197    protected final Map<Integer, OffsetMapping> offsetMappings;
5198   
5199    /**
5200    * The suppression handler to use.
5201    */
5202    protected final SuppressionHandler suppressionHandler;
5203   
5204    /**
5205    * Creates a new resolved version of a dispatcher.
5206    *
5207    * @param adviceMethod The represented advice method.
5208    * @param factories A list of factories to resolve for the parameters of the advice method.
5209    * @param binaryRepresentation The binary representation of the advice method.
5210    * @param throwableType The type to handle by a suppression handler or {@link NoExceptionHandler} to not handle any exceptions.
5211    */
 
5212  395 toggle protected Resolved(MethodDescription.InDefinedShape adviceMethod,
5213    List<OffsetMapping.Factory> factories,
5214    byte[] binaryRepresentation,
5215    TypeDescription throwableType) {
5216  395 this.adviceMethod = adviceMethod;
5217  395 offsetMappings = new HashMap<Integer, OffsetMapping>();
5218  395 for (ParameterDescription.InDefinedShape parameterDescription : adviceMethod.getParameters()) {
5219  702 OffsetMapping offsetMapping = OffsetMapping.Factory.UNDEFINED;
5220  702 for (OffsetMapping.Factory factory : factories) {
5221  6524 OffsetMapping possible = factory.make(parameterDescription);
5222  6516 if (possible != null) {
5223  691 if (offsetMapping == null) {
5224  690 offsetMapping = possible;
5225    } else {
5226  1 throw new IllegalStateException(parameterDescription + " is bound to both " + possible + " and " + offsetMapping);
5227    }
5228    }
5229    }
5230  693 offsetMappings.put(parameterDescription.getOffset(), offsetMapping == null
5231    ? new OffsetMapping.ForParameter(parameterDescription.getIndex(), READ_ONLY, parameterDescription.getType().asErasure())
5232    : offsetMapping);
5233    }
5234  386 this.binaryRepresentation = binaryRepresentation;
5235  386 suppressionHandler = SuppressionHandler.Suppressing.of(throwableType);
5236    }
5237   
 
5238  229 toggle @Override
5239    public boolean isAlive() {
5240  229 return true;
5241    }
5242   
 
5243  415 toggle @Override
5244    public Bound bind(MethodDescription.InDefinedShape instrumentedMethod,
5245    MethodVisitor methodVisitor,
5246    MethodSizeHandler methodSizeHandler,
5247    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler) {
5248  415 return new AdviceMethodInliner(instrumentedMethod,
5249    methodVisitor,
5250    methodSizeHandler,
5251    stackMapFrameHandler,
5252    suppressionHandler.bind(),
5253    new ClassReader(binaryRepresentation));
5254    }
5255   
5256    /**
5257    * Applies a resolution for a given instrumented method.
5258    *
5259    * @param methodVisitor A method visitor for writing byte code to the instrumented method.
5260    * @param methodSizeHandler A handler for computing the method size requirements.
5261    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
5262    * @param instrumentedMethod A description of the instrumented method.
5263    * @param suppressionHandler The bound suppression handler that is used for suppressing exceptions of this advice method.
5264    * @return A method visitor for visiting the advice method's byte code.
5265    */
5266    protected abstract MethodVisitor apply(MethodVisitor methodVisitor,
5267    MethodSizeHandler methodSizeHandler,
5268    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler,
5269    MethodDescription.InDefinedShape instrumentedMethod,
5270    SuppressionHandler.Bound suppressionHandler);
5271   
 
5272  0 toggle @Override
5273    public boolean equals(Object other) {
5274  0 if (this == other) return true;
5275  0 if (other == null || getClass() != other.getClass()) return false;
5276  0 Inlining.Resolved resolved = (Inlining.Resolved) other;
5277  0 return adviceMethod.equals(resolved.adviceMethod) && offsetMappings.equals(resolved.offsetMappings);
5278    }
5279   
 
5280  0 toggle @Override
5281    public int hashCode() {
5282  0 int result = adviceMethod.hashCode();
5283  0 result = 31 * result + offsetMappings.hashCode();
5284  0 return result;
5285    }
5286   
5287    /**
5288    * A bound advice method that copies the code by first extracting the exception table and later appending the
5289    * code of the method without copying any meta data.
5290    */
 
5291    protected class AdviceMethodInliner extends ClassVisitor implements Bound {
5292   
5293    /**
5294    * The instrumented method.
5295    */
5296    private final MethodDescription.InDefinedShape instrumentedMethod;
5297   
5298    /**
5299    * The method visitor for writing the instrumented method.
5300    */
5301    private final MethodVisitor methodVisitor;
5302   
5303    /**
5304    * A handler for computing the method size requirements.
5305    */
5306    private final MethodSizeHandler methodSizeHandler;
5307   
5308    /**
5309    * A handler for translating and injecting stack map frames.
5310    */
5311    private final StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler;
5312   
5313    /**
5314    * A bound suppression handler that is used for suppressing exceptions of this advice method.
5315    */
5316    private final SuppressionHandler.Bound suppressionHandler;
5317   
5318    /**
5319    * A class reader for parsing the class file containing the represented advice method.
5320    */
5321    private final ClassReader classReader;
5322   
5323    /**
5324    * The labels that were found during parsing the method's exception handler in the order of their discovery.
5325    */
5326    private List<Label> labels;
5327   
5328    /**
5329    * Creates a new code copier.
5330    *
5331    * @param instrumentedMethod The instrumented method.
5332    * @param methodVisitor The method visitor for writing the instrumented method.
5333    * @param methodSizeHandler A handler for computing the method size requirements.
5334    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
5335    * @param suppressionHandler A bound suppression handler that is used for suppressing exceptions of this advice method.
5336    * @param classReader A class reader for parsing the class file containing the represented advice method.
5337    */
 
5338  417 toggle protected AdviceMethodInliner(MethodDescription.InDefinedShape instrumentedMethod,
5339    MethodVisitor methodVisitor,
5340    MethodSizeHandler methodSizeHandler,
5341    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler,
5342    SuppressionHandler.Bound suppressionHandler,
5343    ClassReader classReader) {
5344  417 super(Opcodes.ASM5);
5345  417 this.instrumentedMethod = instrumentedMethod;
5346  417 this.methodVisitor = methodVisitor;
5347  417 this.methodSizeHandler = methodSizeHandler;
5348  417 this.stackMapFrameHandler = stackMapFrameHandler;
5349  417 this.suppressionHandler = suppressionHandler;
5350  417 this.classReader = classReader;
5351  417 labels = new ArrayList<Label>();
5352    }
5353   
 
5354  415 toggle @Override
5355    public void prepare() {
5356  415 classReader.accept(new ExceptionTableExtractor(), ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
5357  415 suppressionHandler.onPrepare(methodVisitor);
5358    }
5359   
 
5360  409 toggle @Override
5361    public void apply() {
5362  409 classReader.accept(this, ClassReader.SKIP_DEBUG | stackMapFrameHandler.getReaderHint());
5363    }
5364   
 
5365  1187 toggle @Override
5366    public MethodVisitor visitMethod(int modifiers, String internalName, String descriptor, String signature, String[] exception) {
5367  1187 return adviceMethod.getInternalName().equals(internalName) && adviceMethod.getDescriptor().equals(descriptor)
5368    ? new ExceptionTableSubstitutor(Inlining.Resolved.this.apply(methodVisitor, methodSizeHandler, stackMapFrameHandler, instrumentedMethod, suppressionHandler))
5369    : IGNORE_METHOD;
5370    }
5371   
 
5372  16 toggle @Override
5373    public String toString() {
5374  16 return "Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner{" +
5375    "instrumentedMethod=" + instrumentedMethod +
5376    ", methodVisitor=" + methodVisitor +
5377    ", methodSizeHandler=" + methodSizeHandler +
5378    ", stackMapFrameHandler=" + stackMapFrameHandler +
5379    ", suppressionHandler=" + suppressionHandler +
5380    ", classReader=" + classReader +
5381    ", labels=" + labels +
5382    '}';
5383    }
5384   
5385    /**
5386    * A class visitor that extracts the exception tables of the advice method.
5387    */
 
5388    protected class ExceptionTableExtractor extends ClassVisitor {
5389   
5390    /**
5391    * Creates a new exception table extractor.
5392    */
 
5393  417 toggle protected ExceptionTableExtractor() {
5394  417 super(Opcodes.ASM5);
5395    }
5396   
 
5397  1205 toggle @Override
5398    public MethodVisitor visitMethod(int modifiers, String internalName, String descriptor, String signature, String[] exception) {
5399  1205 return adviceMethod.getInternalName().equals(internalName) && adviceMethod.getDescriptor().equals(descriptor)
5400    ? new ExceptionTableCollector(methodVisitor)
5401    : IGNORE_METHOD;
5402    }
5403   
 
5404  2 toggle @Override
5405    public String toString() {
5406  2 return "Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableExtractor{" +
5407    "methodVisitor=" + methodVisitor +
5408    '}';
5409    }
5410    }
5411   
5412    /**
5413    * A visitor that only writes try-catch-finally blocks to the supplied method visitor. All labels of these tables are collected
5414    * for substitution when revisiting the reminder of the method.
5415    */
 
5416    protected class ExceptionTableCollector extends MethodVisitor {
5417   
5418    /**
5419    * The method visitor for which the try-catch-finally blocks should be written.
5420    */
5421    private final MethodVisitor methodVisitor;
5422   
5423    /**
5424    * Creates a new exception table collector.
5425    *
5426    * @param methodVisitor The method visitor for which the try-catch-finally blocks should be written.
5427    */
 
5428  417 toggle protected ExceptionTableCollector(MethodVisitor methodVisitor) {
5429  417 super(Opcodes.ASM5);
5430  417 this.methodVisitor = methodVisitor;
5431    }
5432   
 
5433  687 toggle @Override
5434    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
5435  687 methodVisitor.visitTryCatchBlock(start, end, handler, type);
5436  687 labels.addAll(Arrays.asList(start, end, handler));
5437    }
5438   
 
5439  0 toggle @Override
5440    public AnnotationVisitor visitTryCatchAnnotation(int typeReference, TypePath typePath, String descriptor, boolean visible) {
5441  0 return methodVisitor.visitTryCatchAnnotation(typeReference, typePath, descriptor, visible);
5442    }
5443   
 
5444  4 toggle @Override
5445    public String toString() {
5446  4 return "Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableCollector{" +
5447    "methodVisitor=" + methodVisitor +
5448    '}';
5449    }
5450    }
5451   
5452    /**
5453    * A label substitutor allows to visit an advice method a second time after the exception handlers were already written.
5454    * Doing so, this visitor substitutes all labels that were already created during the first visit to keep the mapping
5455    * consistent.
5456    */
 
5457    protected class ExceptionTableSubstitutor extends MethodVisitor {
5458   
5459    /**
5460    * A map containing resolved substitutions.
5461    */
5462    private final Map<Label, Label> substitutions;
5463   
5464    /**
5465    * The current index of the visited labels that are used for try-catch-finally blocks.
5466    */
5467    private int index;
5468   
5469    /**
5470    * Creates a label substitor.
5471    *
5472    * @param methodVisitor The method visitor for which to substitute labels.
5473    */
 
5474  394 toggle protected ExceptionTableSubstitutor(MethodVisitor methodVisitor) {
5475  394 super(Opcodes.ASM5, methodVisitor);
5476  394 substitutions = new IdentityHashMap<Label, Label>();
5477    }
5478   
 
5479  664 toggle @Override
5480    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
5481  664 substitutions.put(start, labels.get(index++));
5482  664 substitutions.put(end, labels.get(index++));
5483  664 substitutions.put(handler, labels.get(index++));
5484    }
5485   
 
5486  0 toggle @Override
5487    public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
5488  0 return IGNORE_ANNOTATION;
5489    }
5490   
 
5491  8219 toggle @Override
5492    public void visitLabel(Label label) {
5493  8219 super.visitLabel(resolve(label));
5494    }
5495   
 
5496  0 toggle @Override
5497    public void visitJumpInsn(int opcode, Label label) {
5498  0 super.visitJumpInsn(opcode, resolve(label));
5499    }
5500   
 
5501  0 toggle @Override
5502    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
5503  0 super.visitTableSwitchInsn(min, max, dflt, resolve(labels));
5504    }
5505   
 
5506  0 toggle @Override
5507    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
5508  0 super.visitLookupSwitchInsn(resolve(dflt), keys, resolve(labels));
5509    }
5510   
5511    /**
5512    * Resolves an array of labels.
5513    *
5514    * @param label The labels to resolved.
5515    * @return An array containing the resolved arrays.
5516    */
 
5517  0 toggle private Label[] resolve(Label[] label) {
5518  0 Label[] resolved = new Label[label.length];
5519  0 int index = 0;
5520  0 for (Label aLabel : label) {
5521  0 resolved[index++] = resolve(aLabel);
5522    }
5523  0 return resolved;
5524    }
5525   
5526    /**
5527    * Resolves a single label if mapped or returns the original label.
5528    *
5529    * @param label The label to resolve.
5530    * @return The resolved label.
5531    */
 
5532  15231 toggle private Label resolve(Label label) {
5533  15231 Label substitution = substitutions.get(label);
5534  1719 return substitution == null
5535    ? label
5536    : substitution;
5537    }
5538   
 
5539  6 toggle @Override
5540    public String toString() {
5541  6 return "Advice.Dispatcher.Inlining.Resolved.AdviceMethodInliner.ExceptionTableSubstitutor{" +
5542    "methodVisitor=" + methodVisitor +
5543    ", substitutions=" + substitutions +
5544    ", index=" + index +
5545    '}';
5546    }
5547    }
5548    }
5549   
5550    /**
5551    * A resolved dispatcher for implementing method enter advice.
5552    */
 
5553    protected static class ForMethodEnter extends Inlining.Resolved implements Dispatcher.Resolved.ForMethodEnter {
5554   
5555    /**
5556    * Creates a new resolved dispatcher for implementing method enter advice.
5557    *
5558    * @param adviceMethod The represented advice method.
5559    * @param userFactories A list of user-defined factories for offset mappings.
5560    * @param binaryRepresentation The binary representation of the advice method.
5561    */
 
5562  173 toggle @SuppressWarnings("all") // In absence of @SafeVarargs for Java 6
5563    protected ForMethodEnter(MethodDescription.InDefinedShape adviceMethod,
5564    List<? extends OffsetMapping.Factory> userFactories,
5565    byte[] binaryRepresentation) {
5566  173 super(adviceMethod,
5567    CompoundList.of(Arrays.asList(OffsetMapping.ForParameter.Factory.READ_WRITE,
5568    OffsetMapping.ForBoxedArguments.INSTANCE,
5569    OffsetMapping.ForThisReference.Factory.READ_WRITE,
5570    OffsetMapping.ForField.Factory.READ_WRITE,
5571    OffsetMapping.ForOrigin.Factory.INSTANCE,
5572    OffsetMapping.ForIgnored.INSTANCE,
5573    new OffsetMapping.Illegal(Thrown.class, Enter.class, Return.class, BoxedReturn.class)), userFactories),
5574    binaryRepresentation,
5575    adviceMethod.getDeclaredAnnotations().ofType(OnMethodEnter.class).getValue(SUPPRESS_ENTER, TypeDescription.class));
5576    }
5577   
 
5578  591 toggle @Override
5579    public TypeDescription getEnterType() {
5580  591 return adviceMethod.getReturnType().asErasure();
5581    }
5582   
 
5583  188 toggle @Override
5584    protected MethodVisitor apply(MethodVisitor methodVisitor,
5585    MethodSizeHandler methodSizeHandler,
5586    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler,
5587    MethodDescription.InDefinedShape instrumentedMethod,
5588    SuppressionHandler.Bound suppressionHandler) {
5589  188 Map<Integer, OffsetMapping.Target> offsetMappings = new HashMap<Integer, OffsetMapping.Target>();
5590  188 for (Map.Entry<Integer, OffsetMapping> entry : this.offsetMappings.entrySet()) {
5591  289 offsetMappings.put(entry.getKey(), entry.getValue().resolve(instrumentedMethod, OffsetMapping.Context.ForMethodEntry.of(instrumentedMethod)));
5592    }
5593  175 return new CodeTranslationVisitor.ForMethodEnter(methodVisitor,
5594    methodSizeHandler.bindEntry(adviceMethod),
5595    stackMapFrameHandler.bindEntry(adviceMethod),
5596    instrumentedMethod,
5597    adviceMethod,
5598    offsetMappings,
5599    suppressionHandler);
5600    }
5601   
 
5602  0 toggle @Override
5603    public String toString() {
5604  0 return "Advice.Dispatcher.Inlining.Resolved.ForMethodEnter{" +
5605    "adviceMethod=" + adviceMethod +
5606    ", offsetMappings=" + offsetMappings +
5607    '}';
5608    }
5609    }
5610   
5611    /**
5612    * A resolved dispatcher for implementing method exit advice.
5613    */
 
5614    protected abstract static class ForMethodExit extends Inlining.Resolved implements Dispatcher.Resolved.ForMethodExit {
5615   
5616    /**
5617    * The additional stack size to consider when accessing the local variable array.
5618    */
5619    private final TypeDescription enterType;
5620   
5621    /**
5622    * Creates a new resolved dispatcher for implementing method exit advice.
5623    *
5624    * @param adviceMethod The represented advice method.
5625    * @param userFactories A list of user-defined factories for offset mappings.
5626    * @param binaryRepresentation The binary representation of the advice method.
5627    * @param enterType The type of the value supplied by the enter advice method or
5628    * a description of {@code void} if no such value exists.
5629    */
 
5630  213 toggle protected ForMethodExit(MethodDescription.InDefinedShape adviceMethod,
5631    List<? extends OffsetMapping.Factory> userFactories,
5632    byte[] binaryRepresentation,
5633    TypeDescription enterType) {
5634  213 super(adviceMethod,
5635    CompoundList.of(Arrays.asList(
5636    OffsetMapping.ForParameter.Factory.READ_WRITE,
5637    OffsetMapping.ForBoxedArguments.INSTANCE,
5638    OffsetMapping.ForThisReference.Factory.READ_WRITE,
5639    OffsetMapping.ForField.Factory.READ_WRITE,
5640    OffsetMapping.ForOrigin.Factory.INSTANCE,
5641    OffsetMapping.ForIgnored.INSTANCE,
5642    new OffsetMapping.ForEnterValue.Factory(enterType, false),
5643    OffsetMapping.ForReturnValue.Factory.READ_WRITE,
5644    OffsetMapping.ForBoxedReturnValue.Factory.READ_WRITE,
5645    OffsetMapping.ForThrowable.Factory.of(adviceMethod, false)
5646    ), userFactories),
5647    binaryRepresentation,
5648    adviceMethod.getDeclaredAnnotations().ofType(OnMethodExit.class).getValue(SUPPRESS_EXIT, TypeDescription.class));
5649  213 this.enterType = enterType;
5650    }
5651   
5652    /**
5653    * Resolves exit advice that handles exceptions depending on the specification of the exit advice.
5654    *
5655    * @param adviceMethod The advice method.
5656    * @param userFactories A list of user-defined factories for offset mappings.
5657    * @param binaryRepresentation The binary representation of the advice method.
5658    * @param enterType The type of the value supplied by the enter advice method or
5659    * a description of {@code void} if no such value exists.
5660    * @return An appropriate exit handler.
5661    */
 
5662  219 toggle protected static Resolved.ForMethodExit of(MethodDescription.InDefinedShape adviceMethod,
5663    List<? extends OffsetMapping.Factory> userFactories,
5664    byte[] binaryRepresentation,
5665    TypeDescription enterType) {
5666  219 TypeDescription triggeringThrowable = adviceMethod.getDeclaredAnnotations()
5667    .ofType(OnMethodExit.class)
5668    .getValue(ON_THROWABLE, TypeDescription.class);
5669  219 return triggeringThrowable.represents(NoExceptionHandler.class)
5670    ? new WithoutExceptionHandler(adviceMethod, userFactories, binaryRepresentation, enterType)
5671    : new WithExceptionHandler(adviceMethod, userFactories, binaryRepresentation, enterType, triggeringThrowable);
5672    }
5673   
 
5674  221 toggle @Override
5675    protected MethodVisitor apply(MethodVisitor methodVisitor,
5676    MethodSizeHandler methodSizeHandler,
5677    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler,
5678    MethodDescription.InDefinedShape instrumentedMethod,
5679    SuppressionHandler.Bound suppressionHandler) {
5680  221 Map<Integer, OffsetMapping.Target> offsetMappings = new HashMap<Integer, OffsetMapping.Target>();
5681  221 for (Map.Entry<Integer, OffsetMapping> entry : this.offsetMappings.entrySet()) {
5682  398 offsetMappings.put(entry.getKey(), entry.getValue().resolve(instrumentedMethod, OffsetMapping.Context.ForMethodExit.of(enterType)));
5683    }
5684  217 return new CodeTranslationVisitor.ForMethodExit(methodVisitor,
5685    methodSizeHandler.bindExit(adviceMethod, getTriggeringThrowable().represents(NoExceptionHandler.class)),
5686    stackMapFrameHandler.bindExit(adviceMethod),
5687    instrumentedMethod,
5688    adviceMethod,
5689    offsetMappings,
5690    suppressionHandler,
5691    enterType.getStackSize().getSize() + getPadding().getSize());
5692    }
5693   
5694    /**
5695    * Returns the additional padding this exit advice implies.
5696    *
5697    * @return The additional padding this exit advice implies.
5698    */
5699    protected abstract StackSize getPadding();
5700   
 
5701  0 toggle @Override
5702    public boolean equals(Object other) {
5703  0 return this == other || !(other == null || getClass() != other.getClass())
5704    && super.equals(other)
5705    && enterType == ((Inlining.Resolved.ForMethodExit) other).enterType;
5706    }
5707   
 
5708  0 toggle @Override
5709    public int hashCode() {
5710  0 int result = super.hashCode();
5711  0 result = 31 * result + enterType.hashCode();
5712  0 return result;
5713    }
5714   
5715    /**
5716    * Implementation of exit advice that handles exceptions.
5717    */
 
5718    protected static class WithExceptionHandler extends Inlining.Resolved.ForMethodExit {
5719   
5720    /**
5721    * The type of the handled throwable type for which this advice is invoked.
5722    */
5723    private final TypeDescription triggeringThrowable;
5724   
5725    /**
5726    * Creates a new resolved dispatcher for implementing method exit advice that handles exceptions.
5727    *
5728    * @param adviceMethod The represented advice method.
5729    * @param userFactories A list of user-defined factories for offset mappings.
5730    * @param binaryRepresentation The binary representation of the advice method.
5731    * @param enterType The type of the value supplied by the enter advice method or
5732    * a description of {@code void} if no such value exists.
5733    * @param triggeringThrowable The type of the handled throwable type for which this advice is invoked.
5734    */
 
5735  86 toggle protected WithExceptionHandler(MethodDescription.InDefinedShape adviceMethod,
5736    List<? extends OffsetMapping.Factory> userFactories,
5737    byte[] binaryRepresentation,
5738    TypeDescription enterType,
5739    TypeDescription triggeringThrowable) {
5740  86 super(adviceMethod, userFactories, binaryRepresentation, enterType);
5741  86 this.triggeringThrowable = triggeringThrowable;
5742    }
5743   
 
5744  0 toggle @Override
5745    protected StackSize getPadding() {
5746  0 return triggeringThrowable.getStackSize();
5747    }
5748   
 
5749  278 toggle @Override
5750    public TypeDescription getTriggeringThrowable() {
5751  278 return triggeringThrowable;
5752    }
5753   
 
5754  0 toggle @Override
5755    public String toString() {
5756  0 return "Advice.Dispatcher.Inlining.Resolved.ForMethodExit.WithExceptionHandler{" +
5757    "adviceMethod=" + adviceMethod +
5758    ", offsetMappings=" + offsetMappings +
5759    ", triggeringThrowable=" + triggeringThrowable +
5760    '}';
5761    }
5762    }
5763   
5764    /**
5765    * Implementation of exit advice that ignores exceptions.
5766    */
 
5767    protected static class WithoutExceptionHandler extends Inlining.Resolved.ForMethodExit {
5768   
5769    /**
5770    * Creates a new resolved dispatcher for implementing method exit advice that does not handle exceptions.
5771    *
5772    * @param adviceMethod The represented advice method.
5773    * @param userFactories A list of user-defined factories for offset mappings.
5774    * @param binaryRepresentation The binary representation of the advice method.
5775    * @param enterType The type of the value supplied by the enter advice method or
5776    * a description of {@code void} if no such value exists.
5777    */
 
5778  127 toggle protected WithoutExceptionHandler(MethodDescription.InDefinedShape adviceMethod,
5779    List<? extends OffsetMapping.Factory> userFactories,
5780    byte[] binaryRepresentation,
5781    TypeDescription enterType) {
5782  127 super(adviceMethod, userFactories, binaryRepresentation, enterType);
5783    }
5784   
 
5785  125 toggle @Override
5786    protected StackSize getPadding() {
5787  125 return StackSize.ZERO;
5788    }
5789   
 
5790  260 toggle @Override
5791    public TypeDescription getTriggeringThrowable() {
5792  260 return NoExceptionHandler.DESCRIPTION;
5793    }
5794   
 
5795  0 toggle @Override
5796    public String toString() {
5797  0 return "Advice.Dispatcher.Inlining.Resolved.ForMethodExit.WithoutExceptionHandler{" +
5798    "adviceMethod=" + adviceMethod +
5799    ", offsetMappings=" + offsetMappings +
5800    '}';
5801    }
5802    }
5803    }
5804    }
5805   
5806    /**
5807    * A visitor for translating an advice method's byte code for inlining into the instrumented method.
5808    */
 
5809    protected abstract static class CodeTranslationVisitor extends MethodVisitor implements SuppressionHandler.ReturnValueProducer {
5810   
5811    /**
5812    * A handler for computing the method size requirements.
5813    */
5814    private final MethodSizeHandler.ForAdvice methodSizeHandler;
5815   
5816    /**
5817    * A handler for translating and injecting stack map frames.
5818    */
5819    protected final StackMapFrameHandler.ForAdvice stackMapFrameHandler;
5820   
5821    /**
5822    * The instrumented method.
5823    */
5824    protected final MethodDescription.InDefinedShape instrumentedMethod;
5825   
5826    /**
5827    * The advice method.
5828    */
5829    protected final MethodDescription.InDefinedShape adviceMethod;
5830   
5831    /**
5832    * A mapping of offsets to resolved target offsets in the instrumented method.
5833    */
5834    private final Map<Integer, Resolved.OffsetMapping.Target> offsetMappings;
5835   
5836    /**
5837    * A handler for optionally suppressing exceptions.
5838    */
5839    private final SuppressionHandler.Bound suppressionHandler;
5840   
5841    /**
5842    * A label indicating the end of the advice byte code.
5843    */
5844    protected final Label endOfMethod;
5845   
5846    /**
5847    * Creates a new code translation visitor.
5848    *
5849    * @param methodVisitor A method visitor for writing the instrumented method's byte code.
5850    * @param methodSizeHandler A handler for computing the method size requirements.
5851    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
5852    * @param instrumentedMethod The instrumented method.
5853    * @param adviceMethod The advice method.
5854    * @param offsetMappings A mapping of offsets to resolved target offsets in the instrumented method.
5855    * @param suppressionHandler The suppression handler to use.
5856    */
 
5857  396 toggle protected CodeTranslationVisitor(MethodVisitor methodVisitor,
5858    MethodSizeHandler.ForAdvice methodSizeHandler,
5859    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
5860    MethodDescription.InDefinedShape instrumentedMethod,
5861    MethodDescription.InDefinedShape adviceMethod,
5862    Map<Integer, Resolved.OffsetMapping.Target> offsetMappings,
5863    SuppressionHandler.Bound suppressionHandler) {
5864  396 super(Opcodes.ASM5, methodVisitor);
5865  396 this.methodSizeHandler = methodSizeHandler;
5866  396 this.stackMapFrameHandler = stackMapFrameHandler;
5867  396 this.instrumentedMethod = instrumentedMethod;
5868  396 this.adviceMethod = adviceMethod;
5869  396 this.offsetMappings = offsetMappings;
5870  396 this.suppressionHandler = suppressionHandler;
5871  396 endOfMethod = new Label();
5872    }
5873   
 
5874  0 toggle @Override
5875    public void visitParameter(String name, int modifiers) {
5876    /* do nothing */
5877    }
5878   
 
5879  0 toggle @Override
5880    public AnnotationVisitor visitAnnotationDefault() {
5881  0 return IGNORE_ANNOTATION;
5882    }
5883   
 
5884  478 toggle @Override
5885    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
5886  478 return IGNORE_ANNOTATION;
5887    }
5888   
 
5889  0 toggle @Override
5890    public AnnotationVisitor visitTypeAnnotation(int typeReference, TypePath typePath, String descriptor, boolean visible) {
5891  0 return IGNORE_ANNOTATION;
5892    }
5893   
 
5894  668 toggle @Override
5895    public AnnotationVisitor visitParameterAnnotation(int index, String descriptor, boolean visible) {
5896  668 return IGNORE_ANNOTATION;
5897    }
5898   
 
5899  0 toggle @Override
5900    public void visitAttribute(Attribute attribute) {
5901    /* do nothing */
5902    }
5903   
 
5904  392 toggle @Override
5905    public void visitCode() {
5906  392 suppressionHandler.onStart(mv, methodSizeHandler);
5907    }
5908   
 
5909  6103 toggle @Override
5910    public void visitFrame(int frameType, int localVariableLength, Object[] localVariable, int stackSize, Object[] stack) {
5911  6103 stackMapFrameHandler.translateFrame(mv, frameType, localVariableLength, localVariable, stackSize, stack);
5912    }
5913   
 
5914  377 toggle @Override
5915    public void visitEnd() {
5916  377 suppressionHandler.onEnd(mv, stackMapFrameHandler, this);
5917  377 mv.visitLabel(endOfMethod);
5918  377 onMethodReturn();
5919  377 stackMapFrameHandler.injectCompletionFrame(mv, false);
5920    }
5921   
 
5922  377 toggle @Override
5923    public void visitMaxs(int stackSize, int localVariableLength) {
5924  377 methodSizeHandler.recordMaxima(stackSize, localVariableLength);
5925    }
5926   
 
5927  4019 toggle @Override
5928    public void visitVarInsn(int opcode, int offset) {
5929  4019 Resolved.OffsetMapping.Target target = offsetMappings.get(offset);
5930  4019 if (target != null) {
5931  1203 methodSizeHandler.recordPadding(target.resolveAccess(mv, opcode));
5932    } else {
5933  2816 mv.visitVarInsn(opcode, adjust(offset + instrumentedMethod.getStackSize() - adviceMethod.getStackSize()));
5934    }
5935    }
5936   
 
5937  0 toggle @Override
5938    public void visitIincInsn(int offset, int increment) {
5939  0 Resolved.OffsetMapping.Target target = offsetMappings.get(offset);
5940  0 if (target != null) {
5941  0 methodSizeHandler.recordPadding(target.resolveIncrement(mv, increment));
5942    } else {
5943  0 mv.visitIincInsn(adjust(offset + instrumentedMethod.getStackSize() - adviceMethod.getStackSize()), increment);
5944    }
5945    }
5946   
5947    /**
5948    * Adjusts the offset of a variable instruction within the advice method such that no arguments to
5949    * the instrumented method are overridden.
5950    *
5951    * @param offset The original offset.
5952    * @return The adjusted offset.
5953    */
5954    protected abstract int adjust(int offset);
5955   
5956    @Override
5957    public abstract void visitInsn(int opcode);
5958   
5959    /**
5960    * Invoked after returning from the advice method.
5961    */
5962    protected abstract void onMethodReturn();
5963   
5964    /**
5965    * A code translation visitor that retains the return value of the represented advice method.
5966    */
 
5967    protected static class ForMethodEnter extends CodeTranslationVisitor {
5968   
5969    /**
5970    * Creates a code translation visitor for translating exit advice.
5971    *
5972    * @param methodVisitor A method visitor for writing the instrumented method's byte code.
5973    * @param methodSizeHandler A handler for computing the method size requirements.
5974    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
5975    * @param instrumentedMethod The instrumented method.
5976    * @param adviceMethod The advice method.
5977    * @param offsetMappings A mapping of offsets to resolved target offsets in the instrumented method.
5978    * @param suppressionHandler The suppression handler to use.
5979    */
 
5980  177 toggle protected ForMethodEnter(MethodVisitor methodVisitor,
5981    MethodSizeHandler.ForAdvice methodSizeHandler,
5982    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
5983    MethodDescription.InDefinedShape instrumentedMethod,
5984    MethodDescription.InDefinedShape adviceMethod,
5985    Map<Integer, Resolved.OffsetMapping.Target> offsetMappings,
5986    SuppressionHandler.Bound suppressionHandler) {
5987  177 super(methodVisitor, methodSizeHandler, stackMapFrameHandler, instrumentedMethod, adviceMethod, offsetMappings, suppressionHandler);
5988    }
5989   
 
5990  5527 toggle @Override
5991    public void visitInsn(int opcode) {
5992  5527 switch (opcode) {
5993  0 case Opcodes.RETURN:
5994  0 case Opcodes.IRETURN:
5995  0 case Opcodes.LRETURN:
5996  70 case Opcodes.ARETURN:
5997  0 case Opcodes.FRETURN:
5998  0 case Opcodes.DRETURN:
5999  164 mv.visitJumpInsn(Opcodes.GOTO, endOfMethod);
6000  164 break;
6001  5363 default:
6002  5363 mv.visitInsn(opcode);
6003    }
6004    }
6005   
 
6006  1289 toggle @Override
6007    protected int adjust(int offset) {
6008  1289 return offset;
6009    }
6010   
 
6011  0 toggle @Override
6012    public void onDefaultValue() {
6013  0 if (adviceMethod.getReturnType().represents(boolean.class)
6014    || adviceMethod.getReturnType().represents(byte.class)
6015    || adviceMethod.getReturnType().represents(short.class)
6016    || adviceMethod.getReturnType().represents(char.class)
6017    || adviceMethod.getReturnType().represents(int.class)) {
6018  0 mv.visitInsn(Opcodes.ICONST_0);
6019  0 } else if (adviceMethod.getReturnType().represents(long.class)) {
6020  0 mv.visitInsn(Opcodes.LCONST_0);
6021  0 } else if (adviceMethod.getReturnType().represents(float.class)) {
6022  0 mv.visitInsn(Opcodes.FCONST_0);
6023  0 } else if (adviceMethod.getReturnType().represents(double.class)) {
6024  0 mv.visitInsn(Opcodes.DCONST_0);
6025  0 } else if (!adviceMethod.getReturnType().represents(void.class)) {
6026  0 mv.visitInsn(Opcodes.ACONST_NULL);
6027    }
6028    }
6029   
 
6030  163 toggle @Override
6031    protected void onMethodReturn() {
6032  163 Type returnType = Type.getType(adviceMethod.getReturnType().asErasure().getDescriptor());
6033  85 if (!returnType.equals(Type.VOID_TYPE)) {
6034  85 stackMapFrameHandler.injectReturnFrame(mv);
6035  85 mv.visitVarInsn(returnType.getOpcode(Opcodes.ISTORE), instrumentedMethod.getStackSize());
6036    }
6037    }
6038   
 
6039  2 toggle @Override
6040    public String toString() {
6041  2 return "Advice.Dispatcher.Inlining.CodeTranslationVisitor.ForMethodEnter{" +
6042    "instrumentedMethod=" + instrumentedMethod +
6043    ", adviceMethod=" + adviceMethod +
6044    '}';
6045    }
6046    }
6047   
6048    /**
6049    * A code translation visitor that discards the return value of the represented advice method.
6050    */
 
6051    protected static class ForMethodExit extends CodeTranslationVisitor {
6052   
6053    /**
6054    * The padding after the instrumented method's arguments in the local variable array.
6055    */
6056    private final int padding;
6057   
6058    /**
6059    * Creates a code translation visitor for translating exit advice.
6060    *
6061    * @param methodVisitor A method visitor for writing the instrumented method's byte code.
6062    * @param methodSizeHandler A handler for computing the method size requirements.
6063    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
6064    * @param instrumentedMethod The instrumented method.
6065    * @param adviceMethod The advice method.
6066    * @param offsetMappings A mapping of offsets to resolved target offsets in the instrumented method.
6067    * @param suppressionHandler The suppression handler to use.
6068    * @param padding The padding after the instrumented method's arguments in the local variable array.
6069    */
 
6070  219 toggle protected ForMethodExit(MethodVisitor methodVisitor,
6071    MethodSizeHandler.ForAdvice methodSizeHandler,
6072    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
6073    MethodDescription.InDefinedShape instrumentedMethod,
6074    MethodDescription.InDefinedShape adviceMethod,
6075    Map<Integer, Resolved.OffsetMapping.Target> offsetMappings,
6076    SuppressionHandler.Bound suppressionHandler,
6077    int padding) {
6078  219 super(methodVisitor,
6079    methodSizeHandler,
6080    stackMapFrameHandler,
6081    instrumentedMethod,
6082    adviceMethod,
6083    offsetMappings,
6084    suppressionHandler);
6085  219 this.padding = padding;
6086    }
6087   
 
6088  6585 toggle @Override
6089    public void visitInsn(int opcode) {
6090  6585 switch (opcode) {
6091  0 case Opcodes.RETURN:
6092  0 break;
6093  0 case Opcodes.IRETURN:
6094  0 case Opcodes.ARETURN:
6095  0 case Opcodes.FRETURN:
6096  0 mv.visitInsn(Opcodes.POP);
6097  0 break;
6098  0 case Opcodes.LRETURN:
6099  0 case Opcodes.DRETURN:
6100  0 mv.visitInsn(Opcodes.POP2);
6101  0 break;
6102  6369 default:
6103  6369 mv.visitInsn(opcode);
6104  6369 return;
6105    }
6106  0 mv.visitJumpInsn(Opcodes.GOTO, endOfMethod);
6107    }
6108   
 
6109  0 toggle @Override
6110    protected int adjust(int offset) {
6111  0 return instrumentedMethod.getReturnType().getStackSize().getSize() + padding + offset;
6112    }
6113   
 
6114  0 toggle @Override
6115    public void onDefaultValue() {
6116    /* do nothing */
6117    }
6118   
 
6119  0 toggle @Override
6120    protected void onMethodReturn() {
6121    /* do nothing */
6122    }
6123   
 
6124  5 toggle @Override
6125    public String toString() {
6126  5 return "Advice.Dispatcher.Inlining.CodeTranslationVisitor.ForMethodExit{" +
6127    "instrumentedMethod=" + instrumentedMethod +
6128    ", adviceMethod=" + adviceMethod +
6129    ", padding=" + padding +
6130    '}';
6131    }
6132    }
6133    }
6134    }
6135   
6136    /**
6137    * A dispatcher for an advice method that is being invoked from the instrumented method.
6138    */
 
6139    class Delegating implements Unresolved {
6140   
6141    /**
6142    * The advice method.
6143    */
6144    protected final MethodDescription.InDefinedShape adviceMethod;
6145   
6146    /**
6147    * Creates a new delegating advice dispatcher.
6148    *
6149    * @param adviceMethod The advice method.
6150    */
 
6151  78 toggle protected Delegating(MethodDescription.InDefinedShape adviceMethod) {
6152  78 this.adviceMethod = adviceMethod;
6153    }
6154   
 
6155  48 toggle @Override
6156    public boolean isAlive() {
6157  48 return true;
6158    }
6159   
 
6160  78 toggle @Override
6161    public boolean isBinary() {
6162  78 return false;
6163    }
6164   
 
6165  33 toggle @Override
6166    public Dispatcher.Resolved.ForMethodEnter asMethodEnter(List<? extends OffsetMapping.Factory> userFactories,
6167    ClassFileLocator.Resolution binaryRepresentation) {
6168  33 return new Resolved.ForMethodEnter(adviceMethod, userFactories);
6169    }
6170   
 
6171  45 toggle @Override
6172    public Dispatcher.Resolved.ForMethodExit asMethodExitTo(List<? extends OffsetMapping.Factory> userFactories,
6173    ClassFileLocator.Resolution binaryRepresentation,
6174    Dispatcher.Resolved.ForMethodEnter dispatcher) {
6175  45 return Resolved.ForMethodExit.of(adviceMethod, userFactories, dispatcher.getEnterType());
6176    }
6177   
 
6178  0 toggle @Override
6179    public boolean equals(Object other) {
6180  0 return this == other || !(other == null || getClass() != other.getClass()) && adviceMethod.equals(((Delegating) other).adviceMethod);
6181    }
6182   
 
6183  0 toggle @Override
6184    public int hashCode() {
6185  0 return adviceMethod.hashCode();
6186    }
6187   
 
6188  0 toggle @Override
6189    public String toString() {
6190  0 return "Advice.Dispatcher.Delegating{" +
6191    "adviceMethod=" + adviceMethod +
6192    '}';
6193    }
6194   
6195    /**
6196    * A resolved version of a dispatcher.
6197    */
 
6198    protected abstract static class Resolved implements Dispatcher.Resolved {
6199   
6200    /**
6201    * Indicates a read-only mapping for an offset.
6202    */
6203    private static final boolean READ_ONLY = true;
6204   
6205    /**
6206    * The represented advice method.
6207    */
6208    protected final MethodDescription.InDefinedShape adviceMethod;
6209   
6210    /**
6211    * An unresolved mapping of offsets of the advice method based on the annotations discovered on each method parameter.
6212    */
6213    protected final List<OffsetMapping> offsetMappings;
6214   
6215    /**
6216    * The suppression handler to use.
6217    */
6218    protected final SuppressionHandler suppressionHandler;
6219   
6220    /**
6221    * Creates a new resolved version of a dispatcher.
6222    *
6223    * @param adviceMethod The represented advice method.
6224    * @param factories A list of factories to resolve for the parameters of the advice method.
6225    * @param throwableType The type to handle by a suppression handler or {@link NoExceptionHandler} to not handle any exceptions.
6226    */
 
6227  78 toggle protected Resolved(MethodDescription.InDefinedShape adviceMethod, List<OffsetMapping.Factory> factories, TypeDescription throwableType) {
6228  78 this.adviceMethod = adviceMethod;
6229  78 offsetMappings = new ArrayList<OffsetMapping>(adviceMethod.getParameters().size());
6230  78 for (ParameterDescription.InDefinedShape parameterDescription : adviceMethod.getParameters()) {
6231  299 OffsetMapping offsetMapping = OffsetMapping.Factory.UNDEFINED;
6232  299 for (OffsetMapping.Factory factory : factories) {
6233  2901 OffsetMapping possible = factory.make(parameterDescription);
6234  2896 if (possible != null) {
6235  293 if (offsetMapping == null) {
6236  292 offsetMapping = possible;
6237    } else {
6238  1 throw new IllegalStateException(parameterDescription + " is bound to both " + possible + " and " + offsetMapping);
6239    }
6240    }
6241    }
6242  0 offsetMappings.add(offsetMapping == null
6243    ? new OffsetMapping.ForParameter(parameterDescription.getIndex(), READ_ONLY, parameterDescription.getType().asErasure())
6244    : offsetMapping);
6245    }
6246  72 suppressionHandler = SuppressionHandler.Suppressing.of(throwableType);
6247    }
6248   
 
6249  0 toggle @Override
6250    public boolean isAlive() {
6251  0 return true;
6252    }
6253   
 
6254  72 toggle @Override
6255    public Bound bind(MethodDescription.InDefinedShape instrumentedMethod,
6256    MethodVisitor methodVisitor,
6257    MethodSizeHandler methodSizeHandler,
6258    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler) {
6259  1 if (!adviceMethod.isVisibleTo(instrumentedMethod.getDeclaringType())) {
6260  1 throw new IllegalStateException(adviceMethod + " is not visible to " + instrumentedMethod.getDeclaringType());
6261    }
6262  0 return resolve(instrumentedMethod, methodVisitor, methodSizeHandler, stackMapFrameHandler);
6263    }
6264   
6265    /**
6266    * Binds this dispatcher for resolution to a specific method.
6267    *
6268    * @param instrumentedMethod The instrumented method that is being bound.
6269    * @param methodVisitor The method visitor for writing to the instrumented method.
6270    * @param methodSizeHandler A handler for computing the method size requirements.
6271    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
6272    * @return An appropriate bound advice dispatcher.
6273    */
6274    protected abstract Bound resolve(MethodDescription.InDefinedShape instrumentedMethod,
6275    MethodVisitor methodVisitor,
6276    MethodSizeHandler methodSizeHandler,
6277    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler);
6278   
 
6279  0 toggle @Override
6280    public boolean equals(Object other) {
6281  0 if (this == other) return true;
6282  0 if (other == null || getClass() != other.getClass()) return false;
6283  0 Delegating.Resolved resolved = (Delegating.Resolved) other;
6284  0 return adviceMethod.equals(resolved.adviceMethod) && offsetMappings.equals(resolved.offsetMappings);
6285    }
6286   
 
6287  0 toggle @Override
6288    public int hashCode() {
6289  0 int result = adviceMethod.hashCode();
6290  0 result = 31 * result + offsetMappings.hashCode();
6291  0 return result;
6292    }
6293   
6294    /**
6295    * A bound advice method that copies the code by first extracting the exception table and later appending the
6296    * code of the method without copying any meta data.
6297    */
 
6298    protected abstract static class AdviceMethodWriter implements Bound, SuppressionHandler.ReturnValueProducer {
6299   
6300    /**
6301    * Indicates an empty local variable array which is not required for calling a method.
6302    */
6303    private static final int EMPTY = 0;
6304   
6305    /**
6306    * The advice method.
6307    */
6308    protected final MethodDescription.InDefinedShape adviceMethod;
6309   
6310    /**
6311    * The instrumented method.
6312    */
6313    protected final MethodDescription.InDefinedShape instrumentedMethod;
6314   
6315    /**
6316    * The offset mappings available to this advice.
6317    */
6318    private final List<OffsetMapping.Target> offsetMappings;
6319   
6320    /**
6321    * The method visitor for writing the instrumented method.
6322    */
6323    protected final MethodVisitor methodVisitor;
6324   
6325    /**
6326    * A handler for computing the method size requirements.
6327    */
6328    private final MethodSizeHandler.ForAdvice methodSizeHandler;
6329   
6330    /**
6331    * A handler for translating and injecting stack map frmes.
6332    */
6333    protected final StackMapFrameHandler.ForAdvice stackMapFrameHandler;
6334   
6335    /**
6336    * A bound suppression handler that is used for suppressing exceptions of this advice method.
6337    */
6338    private final SuppressionHandler.Bound suppressionHandler;
6339   
6340    /**
6341    * Creates a new advice method writer.
6342    *
6343    * @param adviceMethod The advice method.
6344    * @param instrumentedMethod The instrumented method.
6345    * @param offsetMappings The offset mappings available to this advice.
6346    * @param methodVisitor The method visitor for writing the instrumented method.
6347    * @param methodSizeHandler A handler for computing the method size requirements.
6348    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
6349    * @param suppressionHandler A bound suppression handler that is used for suppressing exceptions of this advice method.
6350    */
 
6351  0 toggle protected AdviceMethodWriter(MethodDescription.InDefinedShape adviceMethod,
6352    MethodDescription.InDefinedShape instrumentedMethod,
6353    List<OffsetMapping.Target> offsetMappings,
6354    MethodVisitor methodVisitor,
6355    MethodSizeHandler.ForAdvice methodSizeHandler,
6356    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
6357    SuppressionHandler.Bound suppressionHandler) {
6358  0 this.adviceMethod = adviceMethod;
6359  0 this.instrumentedMethod = instrumentedMethod;
6360  0 this.offsetMappings = offsetMappings;
6361  0 this.methodVisitor = methodVisitor;
6362  0 this.methodSizeHandler = methodSizeHandler;
6363  0 this.stackMapFrameHandler = stackMapFrameHandler;
6364  0 this.suppressionHandler = suppressionHandler;
6365    }
6366   
 
6367  0 toggle @Override
6368    public void prepare() {
6369  0 suppressionHandler.onPrepare(methodVisitor);
6370    }
6371   
 
6372  0 toggle @Override
6373    public void apply() {
6374  0 suppressionHandler.onStart(methodVisitor, methodSizeHandler);
6375  0 int index = 0, currentStackSize = 0, maximumStackSize = 0;
6376  0 for (OffsetMapping.Target offsetMapping : offsetMappings) {
6377  0 Type type = Type.getType(adviceMethod.getParameters().get(index++).getType().asErasure().getDescriptor());
6378  0 currentStackSize += type.getSize();
6379  0 maximumStackSize = Math.max(maximumStackSize, currentStackSize + offsetMapping.resolveAccess(methodVisitor, type.getOpcode(Opcodes.ILOAD)));
6380    }
6381  0 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
6382    adviceMethod.getDeclaringType().getInternalName(),
6383    adviceMethod.getInternalName(),
6384    adviceMethod.getDescriptor(),
6385    false);
6386  0 onMethodReturn();
6387  0 suppressionHandler.onEndSkipped(methodVisitor, stackMapFrameHandler, this);
6388  0 stackMapFrameHandler.injectCompletionFrame(methodVisitor, false);
6389  0 methodSizeHandler.recordMaxima(Math.max(maximumStackSize, adviceMethod.getReturnType().getStackSize().getSize()), EMPTY);
6390    }
6391   
6392    /**
6393    * Invoked directly after the advice method was called.
6394    */
6395    protected abstract void onMethodReturn();
6396   
 
6397  0 toggle @Override
6398    public String toString() {
6399  0 return "Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter{" +
6400    "instrumentedMethod=" + instrumentedMethod +
6401    ", methodVisitor=" + methodVisitor +
6402    ", methodSizeHandler=" + methodSizeHandler +
6403    ", stackMapFrameHandler=" + stackMapFrameHandler +
6404    ", suppressionHandler=" + suppressionHandler +
6405    '}';
6406    }
6407   
6408    /**
6409    * An advice method writer for a method entry.
6410    */
 
6411    protected static class ForMethodEnter extends AdviceMethodWriter {
6412   
6413    /**
6414    * Creates a new advice method writer.
6415    *
6416    * @param adviceMethod The advice method.
6417    * @param instrumentedMethod The instrumented method.
6418    * @param offsetMappings The offset mappings available to this advice.
6419    * @param methodVisitor The method visitor for writing the instrumented method.
6420    * @param methodSizeHandler A handler for computing the method size requirements.
6421    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
6422    * @param suppressionHandler A bound suppression handler that is used for suppressing exceptions of this advice method.
6423    */
 
6424  0 toggle protected ForMethodEnter(MethodDescription.InDefinedShape adviceMethod,
6425    MethodDescription.InDefinedShape instrumentedMethod,
6426    List<OffsetMapping.Target> offsetMappings,
6427    MethodVisitor methodVisitor,
6428    MethodSizeHandler.ForAdvice methodSizeHandler,
6429    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
6430    SuppressionHandler.Bound suppressionHandler) {
6431  0 super(adviceMethod, instrumentedMethod, offsetMappings, methodVisitor, methodSizeHandler, stackMapFrameHandler, suppressionHandler);
6432    }
6433   
 
6434  0 toggle @Override
6435    protected void onMethodReturn() {
6436  0 if (adviceMethod.getReturnType().represents(boolean.class)
6437    || adviceMethod.getReturnType().represents(byte.class)
6438    || adviceMethod.getReturnType().represents(short.class)
6439    || adviceMethod.getReturnType().represents(char.class)
6440    || adviceMethod.getReturnType().represents(int.class)) {
6441  0 methodVisitor.visitVarInsn(Opcodes.ISTORE, instrumentedMethod.getStackSize());
6442  0 } else if (adviceMethod.getReturnType().represents(long.class)) {
6443  0 methodVisitor.visitVarInsn(Opcodes.LSTORE, instrumentedMethod.getStackSize());
6444  0 } else if (adviceMethod.getReturnType().represents(float.class)) {
6445  0 methodVisitor.visitVarInsn(Opcodes.FSTORE, instrumentedMethod.getStackSize());
6446  0 } else if (adviceMethod.getReturnType().represents(double.class)) {
6447  0 methodVisitor.visitVarInsn(Opcodes.DSTORE, instrumentedMethod.getStackSize());
6448  0 } else if (!adviceMethod.getReturnType().represents(void.class)) {
6449  0 methodVisitor.visitVarInsn(Opcodes.ASTORE, instrumentedMethod.getStackSize());
6450    }
6451    }
6452   
 
6453  0 toggle @Override
6454    public void onDefaultValue() {
6455  0 if (adviceMethod.getReturnType().represents(boolean.class)
6456    || adviceMethod.getReturnType().represents(byte.class)
6457    || adviceMethod.getReturnType().represents(short.class)
6458    || adviceMethod.getReturnType().represents(char.class)
6459    || adviceMethod.getReturnType().represents(int.class)) {
6460  0 methodVisitor.visitInsn(Opcodes.ICONST_0);
6461  0 methodVisitor.visitVarInsn(Opcodes.ISTORE, instrumentedMethod.getStackSize());
6462  0 } else if (adviceMethod.getReturnType().represents(long.class)) {
6463  0 methodVisitor.visitInsn(Opcodes.LCONST_0);
6464  0 methodVisitor.visitVarInsn(Opcodes.LSTORE, instrumentedMethod.getStackSize());
6465  0 } else if (adviceMethod.getReturnType().represents(float.class)) {
6466  0 methodVisitor.visitInsn(Opcodes.FCONST_0);
6467  0 methodVisitor.visitVarInsn(Opcodes.FSTORE, instrumentedMethod.getStackSize());
6468  0 } else if (adviceMethod.getReturnType().represents(double.class)) {
6469  0 methodVisitor.visitInsn(Opcodes.DCONST_0);
6470  0 methodVisitor.visitVarInsn(Opcodes.DSTORE, instrumentedMethod.getStackSize());
6471  0 } else if (!adviceMethod.getReturnType().represents(void.class)) {
6472  0 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
6473  0 methodVisitor.visitVarInsn(Opcodes.ASTORE, instrumentedMethod.getStackSize());
6474    }
6475    }
6476   
 
6477  0 toggle @Override
6478    public String toString() {
6479  0 return "Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter.ForMethodEnter{" +
6480    "instrumentedMethod=" + instrumentedMethod +
6481    ", adviceMethod=" + adviceMethod +
6482    "}";
6483    }
6484    }
6485   
6486    /**
6487    * An advice method writer for a method exit.
6488    */
 
6489    protected static class ForMethodExit extends AdviceMethodWriter {
6490   
6491    /**
6492    * Creates a new advice method writer.
6493    *
6494    * @param adviceMethod The advice method.
6495    * @param instrumentedMethod The instrumented method.
6496    * @param offsetMappings The offset mappings available to this advice.
6497    * @param methodVisitor The method visitor for writing the instrumented method.
6498    * @param methodSizeHandler A handler for computing the method size requirements.
6499    * @param stackMapFrameHandler A handler for translating and injecting stack map frames.
6500    * @param suppressionHandler A bound suppression handler that is used for suppressing exceptions of this advice method.
6501    */
 
6502  0 toggle protected ForMethodExit(MethodDescription.InDefinedShape adviceMethod,
6503    MethodDescription.InDefinedShape instrumentedMethod,
6504    List<OffsetMapping.Target> offsetMappings,
6505    MethodVisitor methodVisitor,
6506    MethodSizeHandler.ForAdvice methodSizeHandler,
6507    StackMapFrameHandler.ForAdvice stackMapFrameHandler,
6508    SuppressionHandler.Bound suppressionHandler) {
6509  0 super(adviceMethod, instrumentedMethod, offsetMappings, methodVisitor, methodSizeHandler, stackMapFrameHandler, suppressionHandler);
6510    }
6511   
 
6512  0 toggle @Override
6513    protected void onMethodReturn() {
6514  0 switch (adviceMethod.getReturnType().getStackSize()) {
6515  0 case ZERO:
6516  0 return;
6517  0 case SINGLE:
6518  0 methodVisitor.visitInsn(Opcodes.POP);
6519  0 return;
6520  0 case DOUBLE:
6521  0 methodVisitor.visitInsn(Opcodes.POP2);
6522  0 return;
6523  0 default:
6524  0 throw new IllegalStateException("Unexpected size: " + adviceMethod.getReturnType().getStackSize());
6525    }
6526    }
6527   
 
6528  0 toggle @Override
6529    public void onDefaultValue() {
6530    /* do nothing */
6531    }
6532   
 
6533  0 toggle @Override
6534    public String toString() {
6535  0 return "Advice.Dispatcher.Delegating.Resolved.AdviceMethodWriter.ForMethodExit{" +
6536    "instrumentedMethod=" + instrumentedMethod +
6537    ", adviceMethod=" + adviceMethod +
6538    "}";
6539    }
6540    }
6541    }
6542   
6543    /**
6544    * A resolved dispatcher for implementing method enter advice.
6545    */
 
6546    protected static class ForMethodEnter extends Delegating.Resolved implements Dispatcher.Resolved.ForMethodEnter {
6547   
6548    /**
6549    * Creates a new resolved dispatcher for implementing method enter advice.
6550    *
6551    * @param adviceMethod The represented advice method.
6552    * @param userFactories A list of user-defined factories for offset mappings.
6553    */
 
6554  32 toggle @SuppressWarnings("all") // In absence of @SafeVarargs for Java 6
6555    protected ForMethodEnter(MethodDescription.InDefinedShape adviceMethod, List<? extends OffsetMapping.Factory> userFactories) {
6556  32 super(adviceMethod,
6557    CompoundList.of(Arrays.asList(OffsetMapping.ForParameter.Factory.READ_ONLY,
6558    OffsetMapping.ForBoxedArguments.INSTANCE,
6559    OffsetMapping.ForThisReference.Factory.READ_ONLY,
6560    OffsetMapping.ForField.Factory.READ_ONLY,
6561    OffsetMapping.ForOrigin.Factory.INSTANCE,
6562    OffsetMapping.ForIgnored.INSTANCE,
6563    new OffsetMapping.Illegal(Thrown.class, Enter.class, Return.class, BoxedReturn.class)), userFactories),
6564    adviceMethod.getDeclaredAnnotations().ofType(OnMethodEnter.class).getValue(SUPPRESS_ENTER, TypeDescription.class));
6565    }
6566   
 
6567  121 toggle @Override
6568    public TypeDescription getEnterType() {
6569  121 return adviceMethod.getReturnType().asErasure();
6570    }
6571   
 
6572  0 toggle @Override
6573    protected Bound resolve(MethodDescription.InDefinedShape instrumentedMethod,
6574    MethodVisitor methodVisitor,
6575    MethodSizeHandler methodSizeHandler,
6576    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler) {
6577  0 List<OffsetMapping.Target> offsetMappings = new ArrayList<OffsetMapping.Target>(this.offsetMappings.size());
6578  0 for (OffsetMapping offsetMapping : this.offsetMappings) {
6579  0 offsetMappings.add(offsetMapping.resolve(instrumentedMethod, OffsetMapping.Context.ForMethodEntry.of(instrumentedMethod)));
6580    }
6581  0 return new AdviceMethodWriter.ForMethodEnter(adviceMethod,
6582    instrumentedMethod,
6583    offsetMappings,
6584    methodVisitor,
6585    methodSizeHandler.bindEntry(adviceMethod),
6586    stackMapFrameHandler.bindEntry(adviceMethod),
6587    suppressionHandler.bind());
6588    }
6589   
 
6590  0 toggle @Override
6591    public String toString() {
6592  0 return "Advice.Dispatcher.Delegating.Resolved.ForMethodEnter{" +
6593    "adviceMethod=" + adviceMethod +
6594    ", offsetMappings=" + offsetMappings +
6595    '}';
6596    }
6597    }
6598   
6599    /**
6600    * A resolved dispatcher for implementing method exit advice.
6601    */
 
6602    protected abstract static class ForMethodExit extends Delegating.Resolved implements Dispatcher.Resolved.ForMethodExit {
6603   
6604    /**
6605    * The additional stack size to consider when accessing the local variable array.
6606    */
6607    private final TypeDescription enterType;
6608   
6609    /**
6610    * Creates a new resolved dispatcher for implementing method exit advice.
6611    *
6612    * @param adviceMethod The represented advice method.
6613    * @param userFactories A list of user-defined factories for offset mappings.
6614    * @param enterType The type of the value supplied by the enter advice method or
6615    * a description of {@code void} if no such value exists.
6616    */
 
6617  0 toggle protected ForMethodExit(MethodDescription.InDefinedShape adviceMethod,
6618    List<? extends OffsetMapping.Factory> userFactories,
6619    TypeDescription enterType) {
6620  0 super(adviceMethod,
6621    CompoundList.of(Arrays.asList(
6622    OffsetMapping.ForParameter.Factory.READ_ONLY,
6623    OffsetMapping.ForBoxedArguments.INSTANCE,
6624    OffsetMapping.ForThisReference.Factory.READ_ONLY,
6625    OffsetMapping.ForField.Factory.READ_ONLY,
6626    OffsetMapping.ForOrigin.Factory.INSTANCE,
6627    OffsetMapping.ForIgnored.INSTANCE,
6628    new OffsetMapping.ForEnterValue.Factory(enterType, true),
6629    OffsetMapping.ForReturnValue.Factory.READ_ONLY,
6630    OffsetMapping.ForBoxedReturnValue.Factory.READ_ONLY,
6631    OffsetMapping.ForThrowable.Factory.of(adviceMethod, true)
6632    ), userFactories),
6633    adviceMethod.getDeclaredAnnotations().ofType(OnMethodExit.class).getValue(SUPPRESS_EXIT, TypeDescription.class));
6634  0 this.enterType = enterType;
6635    }
6636   
6637    /**
6638    * Resolves exit advice that handles exceptions depending on the specification of the exit advice.
6639    *
6640    * @param adviceMethod The advice method.
6641    * @param userFactories A list of user-defined factories for offset mappings.
6642    * @param enterType The type of the value supplied by the enter advice method or
6643    * a description of {@code void} if no such value exists.
6644    * @return An appropriate exit handler.
6645    */
 
6646  45 toggle protected static Resolved.ForMethodExit of(MethodDescription.InDefinedShape adviceMethod,
6647    List<? extends OffsetMapping.Factory> userFactories,
6648    TypeDescription enterType) {
6649  45 TypeDescription triggeringThrowable = adviceMethod.getDeclaredAnnotations()
6650    .ofType(OnMethodExit.class)
6651    .getValue(ON_THROWABLE, TypeDescription.class);
6652  25 return triggeringThrowable.represents(NoExceptionHandler.class)
6653    ? new WithoutExceptionHandler(adviceMethod, userFactories, enterType)
6654    : new WithExceptionHandler(adviceMethod, userFactories, enterType, triggeringThrowable);
6655    }
6656   
 
6657  0 toggle @Override
6658    protected Bound resolve(MethodDescription.InDefinedShape instrumentedMethod,
6659    MethodVisitor methodVisitor,
6660    MethodSizeHandler methodSizeHandler,
6661    StackMapFrameHandler.ForInstrumentedMethod stackMapFrameHandler) {
6662  0 List<OffsetMapping.Target> offsetMappings = new ArrayList<OffsetMapping.Target>(this.offsetMappings.size());
6663  0 for (OffsetMapping offsetMapping : this.offsetMappings) {
6664  0 offsetMappings.add(offsetMapping.resolve(instrumentedMethod, OffsetMapping.Context.ForMethodExit.of(enterType)));
6665    }
6666  0 return new AdviceMethodWriter.ForMethodExit(adviceMethod,
6667    instrumentedMethod,
6668    offsetMappings,
6669    methodVisitor,
6670    methodSizeHandler.bindExit(adviceMethod, getTriggeringThrowable().represents(NoExceptionHandler.class)),
6671    stackMapFrameHandler.bindExit(adviceMethod),
6672    suppressionHandler.bind());
6673    }
6674   
 
6675  0 toggle @Override
6676    public boolean equals(Object other) {
6677  0 return this == other || !(other == null || getClass() != other.getClass())
6678    && super.equals(other)
6679    && enterType == ((Delegating.Resolved.ForMethodExit) other).enterType;
6680    }
6681   
 
6682  0 toggle @Override
6683    public int hashCode() {
6684  0 int result = super.hashCode();
6685  0 result = 31 * result + enterType.hashCode();
6686  0 return result;
6687    }
6688   
6689    /**
6690    * Implementation of exit advice that handles exceptions.
6691    */
 
6692    protected static class WithExceptionHandler extends Delegating.Resolved.ForMethodExit {
6693   
6694    /**
6695    * The type of the handled throwable type for which this advice is invoked.
6696    */
6697    private final TypeDescription triggeringThrowable;
6698   
6699    /**
6700    * Creates a new resolved dispatcher for implementing method exit advice that handles exceptions.
6701    *
6702    * @param adviceMethod The represented advice method.
6703    * @param userFactories A list of user-defined factories for offset mappings.
6704    * @param enterType The type of the value supplied by the enter advice method or
6705    * a description of {@code void} if no such value exists.
6706    * @param triggeringThrowable The type of the handled throwable type for which this advice is invoked.
6707    */
 
6708  0 toggle protected WithExceptionHandler(MethodDescription.InDefinedShape adviceMethod,
6709    List<? extends OffsetMapping.Factory> userFactories,
6710    TypeDescription enterType,
6711    TypeDescription triggeringThrowable) {
6712  0 super(adviceMethod, userFactories, enterType);
6713  0 this.triggeringThrowable = triggeringThrowable;
6714    }
6715   
 
6716  0 toggle @Override
6717    public TypeDescription getTriggeringThrowable() {
6718  0 return triggeringThrowable;
6719    }
6720   
 
6721  0 toggle @Override
6722    public String toString() {
6723  0 return "Advice.Dispatcher.Delegating.Resolved.ForMethodExit.WithExceptionHandler{" +
6724    "adviceMethod=" + adviceMethod +
6725    ", offsetMappings=" + offsetMappings +
6726    ", triggeringThrowable=" + triggeringThrowable +
6727    '}';
6728    }
6729    }
6730   
6731    /**
6732    * Implementation of exit advice that ignores exceptions.
6733    */
 
6734    protected static class WithoutExceptionHandler extends Delegating.Resolved.ForMethodExit {
6735   
6736    /**
6737    * Creates a new resolved dispatcher for implementing method exit advice that does not handle exceptions.
6738    *
6739    * @param adviceMethod The represented advice method.
6740    * @param userFactories A list of user-defined factories for offset mappings.
6741    * @param enterType The type of the value supplied by the enter advice method or
6742    * a description of {@code void} if no such value exists.
6743    */
 
6744  0 toggle protected WithoutExceptionHandler(MethodDescription.InDefinedShape adviceMethod,
6745    List<? extends OffsetMapping.Factory> userFactories,
6746    TypeDescription enterType) {
6747  0 super(adviceMethod, userFactories, enterType);
6748    }
6749   
 
6750  0 toggle @Override
6751    public TypeDescription getTriggeringThrowable() {
6752  0 return NoExceptionHandler.DESCRIPTION;
6753    }
6754   
 
6755  0 toggle @Override
6756    public String toString() {
6757  0 return "Advice.Dispatcher.Delegating.Resolved.ForMethodExit.WithoutExceptionHandler{" +
6758    "adviceMethod=" + adviceMethod +
6759    ", offsetMappings=" + offsetMappings +
6760    '}';
6761    }
6762    }
6763    }
6764    }
6765    }
6766    }
6767   
6768    /**
6769    * <p>
6770    * Indicates that this method should be inlined before the matched method is invoked. Any class must declare
6771    * at most one method with this annotation. The annotated method must be static. When instrumenting constructors,
6772    * the {@code this} values can only be accessed for writing fields but not for reading fields or invoking methods.
6773    * </p>
6774    * <p>
6775    * The annotated method can return a value that is made accessible to another method annotated by {@link OnMethodExit}.
6776    * </p>
6777    *
6778    * @see Advice
6779    * @see Argument
6780    * @see This
6781    */
6782    @Documented
6783    @Retention(RetentionPolicy.RUNTIME)
6784    @Target(ElementType.METHOD)
 
6785    public @interface OnMethodEnter {
6786   
6787    /**
6788    * Determines if the annotated method should be inlined into the instrumented method or invoked from it. When a method
6789    * is inlined, its byte code is copied into the body of the target method. this makes it is possible to execute code
6790    * with the visibility privileges of the instrumented method while loosing the privileges of the declared method methods.
6791    * When a method is not inlined, it is invoked similarly to a common Java method call. Note that it is not possible to
6792    * set breakpoints within a method when it is inlined as no debugging information is copied from the advice method into
6793    * the instrumented method.
6794    *
6795    * @return {@code true} if the annotated method should be inlined into the instrumented method.
6796    */
6797    boolean inline() default true;
6798   
6799    /**
6800    * Indicates that this advice should suppress any {@link Throwable} type being thrown during the advice's execution.
6801    *
6802    * @return The type of {@link Throwable} to suppress.
6803    */
6804    Class<? extends Throwable> suppress() default NoExceptionHandler.class;
6805    }
6806   
6807    /**
6808    * <p>
6809    * Indicates that this method should be executed before exiting the instrumented method. Any class must declare
6810    * at most one method with this annotation. The annotated method must be static.
6811    * </p>
6812    * <p>
6813    * By default, the annotated method is not invoked if the instrumented method terminates exceptionally. This behavior
6814    * can be changed by setting the {@link OnMethodExit#onThrowable()} property to an exception type for which this advice
6815    * method should be invoked. By setting the value to {@link Throwable}, the advice method is always invoked.
6816    * </p>
6817    *
6818    * @see Advice
6819    * @see Argument
6820    * @see This
6821    * @see Enter
6822    * @see Return
6823    * @see Thrown
6824    */
6825    @Documented
6826    @Retention(RetentionPolicy.RUNTIME)
6827    @Target(ElementType.METHOD)
 
6828    public @interface OnMethodExit {
6829   
6830    /**
6831    * Determines if the annotated method should be inlined into the instrumented method or invoked from it. When a method
6832    * is inlined, its byte code is copied into the body of the target method. this makes it is possible to execute code
6833    * with the visibility privileges of the instrumented method while loosing the privileges of the declared method methods.
6834    * When a method is not inlined, it is invoked similarly to a common Java method call. Note that it is not possible to
6835    * set breakpoints within a method when it is inlined as no debugging information is copied from the advice method into
6836    * the instrumented method.
6837    *
6838    * @return {@code true} if the annotated method should be inlined into the instrumented method.
6839    */
6840    boolean inline() default true;
6841   
6842    /**
6843    * Indicates that this advice should suppress any {@link Throwable} type being thrown during the advice's execution.
6844    *
6845    * @return The type of {@link Throwable} to suppress.
6846    */
6847    Class<? extends Throwable> suppress() default NoExceptionHandler.class;
6848   
6849    /**
6850    * Indicates a {@link Throwable} super type for which this exit advice is invoked if it was thrown from the instrumented method.
6851    * If an exception is thrown, it is available via the {@link Thrown} parameter annotation. If a method returns exceptionally,
6852    * any parameter annotated with {@link Return} is assigned the parameter type's default value.
6853    *
6854    * @return The type of {@link Throwable} for which this exit advice handler is invoked.
6855    */
6856    Class<? extends Throwable> onThrowable() default NoExceptionHandler.class;
6857    }
6858   
6859    /**
6860    * Indicates that the annotated parameter should be mapped to the parameter with index {@link Argument#value()} of
6861    * the instrumented method.
6862    *
6863    * @see Advice
6864    * @see OnMethodEnter
6865    * @see OnMethodExit
6866    */
6867    @Documented
6868    @Retention(RetentionPolicy.RUNTIME)
6869    @Target(ElementType.PARAMETER)
 
6870    public @interface Argument {
6871   
6872    /**
6873    * Returns the index of the mapped parameter.
6874    *
6875    * @return The index of the mapped parameter.
6876    */
6877    int value();
6878   
6879    /**
6880    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated
6881    * type must be equal to the parameter of the instrumented method. If this property is set to {@code true}, the
6882    * annotated parameter can be any super type of the instrumented methods parameter.
6883    *
6884    * @return {@code true} if this parameter is read-only.
6885    */
6886    boolean readOnly() default true;
6887    }
6888   
6889    /**
6890    * <p>
6891    * Indicates that the annotated parameter should be mapped to the {@code this} reference of the instrumented method.
6892    * </p>
6893    * <p>
6894    * <b>Important</b>: Parameters with this option must not be used when from a constructor in combination with
6895    * {@link OnMethodEnter} where the {@code this} reference is not available.
6896    * </p>
6897    *
6898    * @see Advice
6899    * @see OnMethodEnter
6900    * @see OnMethodExit
6901    */
6902    @Documented
6903    @Retention(RetentionPolicy.RUNTIME)
6904    @Target(ElementType.PARAMETER)
 
6905    public @interface This {
6906   
6907    /**
6908    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated
6909    * type must be equal to the type declaring the instrumented method. If this property is set to {@code true}, the
6910    * annotated parameter can be any super type of the instrumented method's declaring type.
6911    *
6912    * @return {@code true} if this parameter is read-only.
6913    */
6914    boolean readOnly() default true;
6915   
6916    /**
6917    * Determines if the parameter should be assigned {@code null} if the instrumented method is static.
6918    *
6919    * @return {@code true} if the value assignment is optional.
6920    */
6921    boolean optional() default false;
6922    }
6923   
6924    /**
6925    * <p>
6926    * Indicates that the annotated parameter should be mapped to a field in the scope of the instrumented method.
6927    * </p>
6928    * <p>
6929    * <b>Important</b>: Parameters with this option must not be used when from a constructor in combination with
6930    * {@link OnMethodEnter} and a non-static field where the {@code this} reference is not available.
6931    * </p>
6932    * <p>
6933    * <b>Note</b>: As the mapping is virtual, Byte Buddy might be required to reserve more space on the operand stack than the
6934    * optimal value when accessing this parameter. This does not normally matter as the additional space requirement is minimal.
6935    * However, if the runtime performance of class creation is secondary, one can require ASM to recompute the optimal frames by
6936    * setting {@link ClassWriter#COMPUTE_MAXS}. This is however only relevant when writing to a non-static field.
6937    * </p>
6938    *
6939    * @see Advice
6940    * @see OnMethodEnter
6941    * @see OnMethodExit
6942    */
6943    @Documented
6944    @Retention(RetentionPolicy.RUNTIME)
6945    @Target(ElementType.PARAMETER)
 
6946    public @interface FieldValue {
6947   
6948    /**
6949    * Returns the name of the field.
6950    *
6951    * @return The name of the field.
6952    */
6953    String value();
6954   
6955    /**
6956    * Returns the type that declares the field that should be mapped to the annotated parameter. If this property
6957    * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy.
6958    *
6959    * @return The type that declares the field or {@code void} if this type should be determined implicitly.
6960    */
6961    Class<?> declaringType() default void.class;
6962   
6963    /**
6964    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated
6965    * type must be equal to the mapped field type. If this property is set to {@code true}, the annotated parameter
6966    * can be any super type of the field type.
6967    *
6968    * @return {@code true} if this parameter is read-only.
6969    */
6970    boolean readOnly() default true;
6971    }
6972   
6973    /**
6974    * Indicates that the annotated parameter should be mapped to a string representation of the instrumented method or
6975    * to a constant representing the {@link Class} declaring the method.
6976    *
6977    * @see Advice
6978    * @see OnMethodEnter
6979    * @see OnMethodExit
6980    */
6981    @Documented
6982    @Retention(RetentionPolicy.RUNTIME)
6983    @Target(ElementType.PARAMETER)
 
6984    public @interface Origin {
6985   
6986    /**
6987    * Indicates that the origin string should be indicated by the {@link Object#toString()} representation of the instrumented method.
6988    */
6989    String DEFAULT = "";
6990   
6991    /**
6992    * Returns the pattern the annotated parameter should be assigned. By default, the {@link Origin#toString()} representation
6993    * of the method is assigned. Alternatively, a pattern can be assigned where:
6994    * <ul>
6995    * <li>{@code #t} inserts the method's declaring type.</li>
6996    * <li>{@code #m} inserts the name of the method ({@code <init>} for constructors and {@code <clinit>} for static initializers).</li>
6997    * <li>{@code #d} for the method's descriptor.</li>
6998    * <li>{@code #s} for the method's signature.</li>
6999    * <li>{@code #r} for the method's return type.</li>
7000    * </ul>
7001    * Any other {@code #} character must be escaped by {@code \} which can be escaped by itself. This property is ignored if the annotated
7002    * parameter is of type {@link Class}.
7003    *
7004    * @return The pattern the annotated parameter should be assigned.
7005    */
7006    String value() default DEFAULT;
7007    }
7008   
7009    /**
7010    * Indicates that the annotated parameter should always return a default value (i.e. {@code 0} for numeric values, {@code false}
7011    * for {@code boolean} types and {@code null} for reference types).
7012    *
7013    * @see Advice
7014    * @see OnMethodEnter
7015    * @see OnMethodExit
7016    */
7017    @Documented
7018    @Retention(RetentionPolicy.RUNTIME)
7019    @Target(ElementType.PARAMETER)
 
7020    public @interface Ignored {
7021    /* empty */
7022    }
7023   
7024    /**
7025    * Indicates that the annotated parameter should be mapped to the value that is returned by the advice method that is annotated
7026    * by {@link OnMethodEnter}.
7027    *
7028    * @see Advice
7029    * @see OnMethodExit
7030    */
7031    @Documented
7032    @Retention(RetentionPolicy.RUNTIME)
7033    @Target(ElementType.PARAMETER)
 
7034    public @interface Enter {
7035   
7036    /**
7037    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated
7038    * type must be equal to the parameter of the instrumented method. If this property is set to {@code true}, the
7039    * annotated parameter can be any super type of the instrumented methods parameter.
7040    *
7041    * @return {@code true} if this parameter is read-only.
7042    */
7043    boolean readOnly() default true;
7044    }
7045   
7046    /**
7047    * Indicates that the annotated parameter should be mapped to the return value of the instrumented method. If the instrumented
7048    * method terminates exceptionally, the type's default value is assigned to the parameter, i.e. {@code 0} for numeric types
7049    * and {@code null} for reference types.
7050    *
7051    * @see Advice
7052    * @see OnMethodExit
7053    */
7054    @Documented
7055    @Retention(RetentionPolicy.RUNTIME)
7056    @Target(ElementType.PARAMETER)
 
7057    public @interface Return {
7058   
7059    /**
7060    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated
7061    * type must be equal to the parameter of the instrumented method. If this property is set to {@code true}, the
7062    * annotated parameter can be any super type of the instrumented methods parameter.
7063    *
7064    * @return {@code true} if this parameter is read-only.
7065    */
7066    boolean readOnly() default true;
7067    }
7068   
7069    /**
7070    * <p>
7071    * Indicates that the annotated parameter should be mapped to a the return value where primitive types are boxed. For this
7072    * to be possible, the annotated parameter must be of type {@link Object}.
7073    * </p>
7074    * <p>
7075    * Note that accessing this parameter is merely virtual. A new array is created on every access. As a result, changes to the
7076    * array have no effect other than for the local copy and when accessing the array twice, the equality relation does not hold.
7077    * For example, for {@code @Advice.BoxedReturn Object foo}, the relation {@code foo == foo} does not necessarily hold for primitive
7078    * types. For avoiding additional allocations, the array needs to be stored in a separate local variable. The variable itself is
7079    * always read only.
7080    * </p>
7081    * <p>
7082    * <b>Note</b>: As the mapping is virtual, Byte Buddy might be required to reserve more space on the operand stack than the
7083    * optimal value when accessing this parameter. This does not normally matter as the additional space requirement is minimal.
7084    * However, if the runtime performance of class creation is secondary, one can require ASM to recompute the optimal frames by
7085    * setting {@link ClassWriter#COMPUTE_MAXS}.
7086    * </p>
7087    *
7088    * @see Advice
7089    * @see OnMethodEnter
7090    * @see OnMethodExit
7091    */
7092    @Documented
7093    @Retention(RetentionPolicy.RUNTIME)
7094    @Target(ElementType.PARAMETER)
 
7095    public @interface BoxedReturn {
7096   
7097    /**
7098    * Determines if it should be possible to write to the boxed return value. When writing to the return value, the assigned type
7099    * is casted to the instrumented method's return type. If the method's return type is primitive, the value is unboxed from the
7100    * primitive return type's wrapper type after casting to the latter type. If the method is {@code void}, the assigned value is
7101    * simply dropped.
7102    *
7103    * @return {@code true} if it should be possible to write to the annotated parameter.
7104    */
7105    boolean readOnly() default true;
7106    }
7107   
7108    /**
7109    * <p>
7110    * Indicates that the annotated parameter should be mapped to an array containing a (boxed) version of all arguments of the
7111    * method being instrumented. It is required that the annotated parameter is an array of type {@link Object}.
7112    * </p>
7113    * <p>
7114    * Note that accessing this parameter is merely virtual. A new array is created on every access. As a result, changes to the
7115    * array have no effect other than for the local copy and when accessing the array twice, the equality relation does not hold.
7116    * For example, for {@code @Advice.BoxedArguments Object[] foo}, the relation {@code foo == foo} does not hold. For avoiding
7117    * new allocations, the array needs to be stored in a separate local variable. The variable itself is always read only.
7118    * </p>
7119    * <p>
7120    * <b>Note</b>: As the mapping is virtual, Byte Buddy might be required to reserve more space on the operand stack than the
7121    * optimal value when accessing this parameter. This does not normally matter as the additional space requirement is minimal.
7122    * However, if the runtime performance of class creation is secondary, one can require ASM to recompute the optimal frames by
7123    * setting {@link ClassWriter#COMPUTE_MAXS}.
7124    * </p>
7125    *
7126    * @see Advice
7127    * @see OnMethodEnter
7128    * @see OnMethodExit
7129    */
7130    @Documented
7131    @Retention(RetentionPolicy.RUNTIME)
7132    @Target(ElementType.PARAMETER)
 
7133    public @interface BoxedArguments {
7134    /* boxed */
7135    }
7136   
7137    /**
7138    * Indicates that the annotated parameter should be mapped to the return value of the instrumented method. For this to be valid,
7139    * the parameter must be of type {@link Throwable}. If the instrumented method terminates regularly, {@code null} is assigned to
7140    * the annotated parameter.
7141    *
7142    * @see Advice
7143    * @see OnMethodExit
7144    */
7145    @Documented
7146    @Retention(RetentionPolicy.RUNTIME)
7147    @Target(ElementType.PARAMETER)
 
7148    public @interface Thrown {
7149   
7150    /**
7151    * <p>
7152    * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, it is illegal to
7153    * write to the annotated parameter. If this property is set to {@code true}, the annotated parameter can either be set
7154    * to {@code null} to suppress an exception that was thrown by the adviced method or it can be set to any other exception
7155    * that will be thrown after the advice method returned.
7156    * </p>
7157    * <p>
7158    * If an exception is suppressed, the default value for the return type is returned from the method, i.e. {@code 0} for any
7159    * numeric type and {@code null} for a reference type. The default value can be replaced via the {@link Return} annotation.
7160    * </p>
7161    *
7162    * @return {@code true} if this parameter is read-only.
7163    */
7164    boolean readOnly() default true;
7165    }
7166   
7167    /**
7168    * <p>
7169    * A dynamic value allows to bind parameters of an {@link Advice} method to a custom, constant value.
7170    * </p>
7171    * <p>The mapped value must be a constant value that can be embedded into a Java class file. This holds for all primitive types,
7172    * instances of {@link String} and for {@link Class} instances as well as their unloaded {@link TypeDescription} representations.
7173    * </p>
7174    *
7175    * @param <T> The type of the annotation this dynamic value requires to provide a mapping.
7176    * @see WithCustomMapping
7177    */
 
7178    public interface DynamicValue<T extends Annotation> {
7179   
7180    /**
7181    * Resolves a constant value that is mapped to a parameter that is annotated with a custom bound annotation.
7182    *
7183    * @param instrumentedMethod The instrumented method onto which this advice is applied.
7184    * @param target The target parameter that is bound.
7185    * @param annotation The annotation that triggered this binding.
7186    * @param initialized {@code true} if the method is initialized when the value is bound, i.e. that the value is not
7187    * supplied to a constructor before the super constructor was invoked.
7188    * @return The constant pool value that is bound to the supplied parameter or {@code null} to assign this value.
7189    */
7190    Object resolve(MethodDescription.InDefinedShape instrumentedMethod,
7191    ParameterDescription.InDefinedShape target,
7192    AnnotationDescription.Loadable<T> annotation,
7193    boolean initialized);
7194   
7195    /**
7196    * <p>
7197    * A {@link DynamicValue} implementation that always binds a fixed value.
7198    * </p>
7199    * <p>
7200    * The mapped value must be a constant value that can be embedded into a Java class file. This holds for all primitive types,
7201    * instances of {@link String} and for {@link Class} instances as well as their unloaded {@link TypeDescription} representations.
7202    * </p>
7203    */
 
7204    class ForFixedValue implements DynamicValue<Annotation> {
7205   
7206    /**
7207    * The fixed value to bind to the corresponding annotation.
7208    */
7209    private final Object value;
7210   
7211    /**
7212    * Creates a dynamic value for a fixed value.
7213    *
7214    * @param value The fixed value to bind to the corresponding annotation.
7215    */
 
7216  43 toggle public ForFixedValue(Object value) {
7217  43 this.value = value;
7218    }
7219   
 
7220  0 toggle @Override
7221    public Object resolve(MethodDescription.InDefinedShape instrumentedMethod,
7222    ParameterDescription.InDefinedShape target,
7223    AnnotationDescription.Loadable<Annotation> annotation,
7224    boolean initialized) {
7225  0 return value;
7226    }
7227   
 
7228  5 toggle @Override
7229    public boolean equals(Object object) {
7230  1 if (this == object) return true;
7231  2 if (object == null || getClass() != object.getClass()) return false;
7232  2 ForFixedValue that = (ForFixedValue) object;
7233  2 return value != null ? value.equals(that.value) : that.value == null;
7234    }
7235   
 
7236  3 toggle @Override
7237    public int hashCode() {
7238  3 return value != null ? value.hashCode() : 0;
7239    }
7240   
 
7241  3 toggle @Override
7242    public String toString() {
7243  3 return "Advice.DynamicValue.ForFixedValue{" +
7244    "value=" + value +
7245    '}';
7246    }
7247    }
7248    }
7249   
7250    /**
7251    * A builder step for creating an {@link Advice} that uses custom mappings of annotations to constant pool values.
7252    */
 
7253    public static class WithCustomMapping {
7254   
7255    /**
7256    * A map containing dynamically computed constant pool values that are mapped by their triggering annotation type.
7257    */
7258    private final Map<Class<? extends Annotation>, DynamicValue<?>> dynamicValues;
7259   
7260    /**
7261    * Creates a new custom mapping builder step without including any custom mappings.
7262    */
 
7263  50 toggle protected WithCustomMapping() {
7264  50 this(Collections.<Class<? extends Annotation>, DynamicValue<?>>emptyMap());
7265    }
7266   
7267    /**
7268    * Creates a new custom mapping builder step with the given custom mappings.
7269    *
7270    * @param dynamicValues A map containing dynamically computed constant pool values that are mapped by their triggering annotation type.
7271    */
 
7272  100 toggle protected WithCustomMapping(Map<Class<? extends Annotation>, DynamicValue<?>> dynamicValues) {
7273  100 this.dynamicValues = dynamicValues;
7274    }
7275   
7276    /**
7277    * Binds an annotation type to dynamically computed value. Whenever the {@link Advice} component discovers the given annotation on
7278    * a parameter of an advice method, the dynamic value is asked to provide a value that is then assigned to the parameter in question.
7279    *
7280    * @param type The annotation type that triggers the mapping.
7281    * @param dynamicValue The dynamic value that is computed for binding the parameter to a value.
7282    * @param <T> The annotation type.
7283    * @return A new builder for an advice that considers the supplied annotation type during binding.
7284    */
 
7285  49 toggle public <T extends Annotation> WithCustomMapping bind(Class<? extends T> type, DynamicValue<T> dynamicValue) {
7286  49 Map<Class<? extends Annotation>, DynamicValue<?>> dynamicValues = new HashMap<Class<? extends Annotation>, Advice.DynamicValue<?>>(this.dynamicValues);
7287  49 if (!type.isAnnotation()) {
7288  1 throw new IllegalArgumentException("Not an annotation type: " + type);
7289  48 } else if (dynamicValues.put(type, dynamicValue) != null) {
7290  1 throw new IllegalArgumentException("Annotation-type already mapped: " + type);
7291    }
7292  47 return new WithCustomMapping(dynamicValues);
7293    }
7294   
7295    /**
7296    * Implements advice where every matched method is advised by the given type's advisory methods. The advices binary representation is
7297    * accessed by querying the class loader of the supplied class for a class file.
7298    *
7299    * @param type The type declaring the advice.
7300    * @return A method visitor wrapper representing the supplied advice.
7301    */
 
7302  46 toggle public Advice to(Class<?> type) {
7303  46 return to(type, ClassFileLocator.ForClassLoader.of(type.getClassLoader()));
7304    }
7305   
7306    /**
7307    * Implements advice where every matched method is advised by the given type's advisory methods.
7308    *
7309    * @param type The type declaring the advice.
7310    * @param classFileLocator The class file locator for locating the advisory class's class file.
7311    * @return A method visitor wrapper representing the supplied advice.
7312    */
 
7313  46 toggle public Advice to(Class<?> type, ClassFileLocator classFileLocator) {
7314  46 return to(new TypeDescription.ForLoadedType(type), classFileLocator);
7315    }
7316   
7317    /**
7318    * Implements advice where every matched method is advised by the given type's advisory methods.
7319    *
7320    * @param typeDescription A description of the type declaring the advice.
7321    * @param classFileLocator The class file locator for locating the advisory class's class file.
7322    * @return A method visitor wrapper representing the supplied advice.
7323    */
 
7324  46 toggle public Advice to(TypeDescription typeDescription, ClassFileLocator classFileLocator) {
7325  46 List<Dispatcher.OffsetMapping.Factory> userFactories = new ArrayList<Dispatcher.OffsetMapping.Factory>(dynamicValues.size());
7326  46 for (Map.Entry<Class<? extends Annotation>, DynamicValue<?>> entry : dynamicValues.entrySet()) {
7327  46 userFactories.add(Dispatcher.OffsetMapping.ForUserValue.Factory.of(entry.getKey(), entry.getValue()));
7328    }
7329  46 return Advice.to(typeDescription, classFileLocator, userFactories);
7330    }
7331   
 
7332  9 toggle @Override
7333    public boolean equals(Object object) {
7334  2 if (this == object) return true;
7335  4 if (object == null || getClass() != object.getClass()) return false;
7336  3 WithCustomMapping that = (WithCustomMapping) object;
7337  3 return dynamicValues.equals(that.dynamicValues);
7338    }
7339   
 
7340  5 toggle @Override
7341    public int hashCode() {
7342  5 return dynamicValues.hashCode();
7343    }
7344   
 
7345  6 toggle @Override
7346    public String toString() {
7347  6 return "Advice.WithCustomMapping{" +
7348    "dynamicValues=" + dynamicValues +
7349    '}';
7350    }
7351    }
7352   
7353    /**
7354    * A marker class that indicates that an advice method does not suppress any {@link Throwable}.
7355    */
 
7356    private static class NoExceptionHandler extends Throwable {
7357   
7358    /**
7359    * A description of the {@link NoExceptionHandler} type.
7360    */
7361    private static final TypeDescription DESCRIPTION = new TypeDescription.ForLoadedType(NoExceptionHandler.class);
7362   
7363    /**
7364    * A private constructor as this class is not supposed to be invoked.
7365    */
 
7366  1 toggle private NoExceptionHandler() {
7367  1 throw new UnsupportedOperationException("This marker class is not supposed to be instantiated");
7368    }
7369    }
7370    }